fix: completion window improvements

- fixed a bug which resulted in selected completion item not being indicated properly (closes #1242)
- added option to set selected completion item background color in color schemes
This commit is contained in:
Akash Yadav
2023-09-06 13:10:21 +05:30
parent 3a2a309cf3
commit 76cba7327b
6 changed files with 132 additions and 91 deletions
@@ -16,9 +16,9 @@
*/
package com.itsaky.androidide.editor.adapters
import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
import android.text.TextUtils
import android.util.TypedValue
import android.view.LayoutInflater
@@ -29,7 +29,6 @@ import com.itsaky.androidide.editor.R
import com.itsaky.androidide.editor.databinding.LayoutCompletionItemBinding
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.lsp.models.ClassCompletionData
import com.itsaky.androidide.lsp.models.CompletionItem as LspCompletionItem
import com.itsaky.androidide.lsp.models.CompletionItemKind.CLASS
import com.itsaky.androidide.lsp.models.CompletionItemKind.CONSTRUCTOR
import com.itsaky.androidide.lsp.models.CompletionItemKind.ENUM
@@ -49,26 +48,19 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.COMPLETION_WND
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.COMPLETION_WND_TEXT_TYPE
import com.itsaky.androidide.tasks.executeAsync
import com.itsaky.androidide.utils.customOrJBMono
import com.itsaky.androidide.utils.resolveAttr
import com.itsaky.androidide.xml.versions.ApiVersions
import com.itsaky.androidide.xml.versions.Info
import io.github.rosemoe.sora.lang.completion.CompletionItem
import io.github.rosemoe.sora.widget.component.EditorAutoCompletion
import io.github.rosemoe.sora.widget.component.EditorCompletionAdapter
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
import com.itsaky.androidide.lsp.models.CompletionItem as LspCompletionItem
class CompletionListAdapter : EditorCompletionAdapter() {
override fun attachValues(window: EditorAutoCompletion, items: List<CompletionItem>) {
super.attachValues(window, items)
}
override fun getItemHeight(): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
40f,
Resources.getSystem().displayMetrics
)
TypedValue.COMPLEX_UNIT_DIP,
40f,
Resources.getSystem().displayMetrics
)
.toInt()
}
@@ -97,12 +89,7 @@ class CompletionListAdapter : EditorCompletionAdapter() {
if (desc.isEmpty()) {
binding.completionDetail.visibility = View.GONE
}
binding.root.setBackgroundColor(
context.resolveAttr(
if (isCurrentCursorPosition) R.color.completionList_backgroundSelected
else R.color.completionList_background
)
)
binding.completionApiInfo.visibility = View.GONE
applyColorScheme(binding, isCurrentCursorPosition)
@@ -111,15 +98,8 @@ class CompletionListAdapter : EditorCompletionAdapter() {
}
private fun applyColorScheme(binding: LayoutCompletionItemBinding, isCurrent: Boolean) {
var color =
if (isCurrent) getThemeColor(SchemeAndroidIDE.COMPLETION_WND_BG_CURRENT_ITEM)
else getThemeColor(EditorColorScheme.COMPLETION_WND_BACKGROUND)
if (color != 0) {
binding.root.backgroundTintList = ColorStateList.valueOf(color)
binding.completionIconText.backgroundTintList = ColorStateList.valueOf(color)
}
color = getThemeColor(COMPLETION_WND_TEXT_LABEL)
setItemBackground(binding, isCurrent)
var color = getThemeColor(COMPLETION_WND_TEXT_LABEL)
if (color != 0) {
binding.completionLabel.setTextColor(color)
binding.completionIconText.setTextColor(color)
@@ -141,6 +121,22 @@ class CompletionListAdapter : EditorCompletionAdapter() {
}
}
private fun setItemBackground(binding: LayoutCompletionItemBinding, isCurrent: Boolean) {
val color =
if (isCurrent) getThemeColor(SchemeAndroidIDE.COMPLETION_WND_BG_CURRENT_ITEM)
else 0
val cornerRadius = binding.root.context.resources
.getDimensionPixelSize(R.dimen.completion_window_corner_radius).toFloat()
val gd = GradientDrawable().apply {
setColor(color)
setCornerRadius(cornerRadius)
}
binding.root.background = gd
}
private fun showApiInfoIfNeeded(item: LspCompletionItem, textView: TextView) {
executeAsync({
if (!isValidForApiVersion(item)) {
@@ -212,15 +208,15 @@ class CompletionListAdapter : EditorCompletionAdapter() {
val data = item.data
return if ( // These represent a class type
(type === CLASS ||
type === INTERFACE ||
type === ENUM ||
type === INTERFACE ||
type === ENUM ||
// These represent a method type
type === METHOD ||
type === CONSTRUCTOR ||
// These represent a method type
type === METHOD ||
type === CONSTRUCTOR ||
// A field type
type === FIELD) && data != null
// A field type
type === FIELD) && data != null
) {
val className =
when (data) {
@@ -72,6 +72,10 @@ class SchemeParser(private val resolveFileRef: (String) -> File) {
"completion.window.text.type",
SchemeAndroidIDE.COMPLETION_WND_TEXT_TYPE
),
COMPLETION_WND_BG_CURRENT_ITEM(
"completion.window.item.current",
SchemeAndroidIDE.COMPLETION_WND_BG_CURRENT_ITEM
),
NON_PRINTABLE_CHAR("non_printable_char", EditorColorScheme.NON_PRINTABLE_CHAR),
HIGHLIGHTED_DELIMITERS_UNDERLINE(
"highlighted.delimiters.underline",
@@ -19,7 +19,9 @@ package com.itsaky.androidide.editor.ui
import android.graphics.drawable.GradientDrawable
import android.view.ViewGroup
import com.blankj.utilcode.util.SizeUtils.dp2px
import android.view.ViewGroup.MarginLayoutParams
import androidx.core.view.updateLayoutParams
import com.itsaky.androidide.editor.R
import io.github.rosemoe.sora.widget.component.DefaultCompletionLayout
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
@@ -32,14 +34,27 @@ import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
class EditorCompletionLayout : DefaultCompletionLayout() {
override fun onApplyColorScheme(colorScheme: EditorColorScheme) {
super.onApplyColorScheme(colorScheme)
val gd =
(completionList.parent as? ViewGroup?)?.background as? GradientDrawable?
?: throw RuntimeException(
"CompletionLayout implementation changed. Please report this issue."
)
val resources = completionList.context.resources
val cornerRadius = resources.getDimensionPixelSize(R.dimen.completion_window_corner_radius)
.toFloat()
gd.setStroke(dp2px(1f), colorScheme.getColor(EditorColorScheme.COMPLETION_WND_CORNER))
val strokeWidth = resources
.getDimensionPixelSize(R.dimen.completion_window_stroke_width)
(completionList.parent as? ViewGroup?)?.background = GradientDrawable().apply {
setCornerRadius(cornerRadius)
setStroke(strokeWidth, colorScheme.getColor(EditorColorScheme.COMPLETION_WND_CORNER))
setColor(colorScheme.getColor(EditorColorScheme.COMPLETION_WND_BACKGROUND))
}
if (completionList.layoutParams is MarginLayoutParams) {
completionList.updateLayoutParams<MarginLayoutParams> {
marginStart = strokeWidth
topMargin = strokeWidth
marginEnd = strokeWidth
bottomMargin = strokeWidth
}
}
}
}
@@ -27,8 +27,7 @@ import io.github.rosemoe.sora.lang.completion.CompletionItem
import io.github.rosemoe.sora.lang.completion.CompletionPublisher
import io.github.rosemoe.sora.widget.component.CompletionLayout
import io.github.rosemoe.sora.widget.component.EditorAutoCompletion
import io.github.rosemoe.sora.widget.component.EditorCompletionAdapter
import java.util.concurrent.atomic.AtomicReference
import java.lang.ref.WeakReference
import kotlin.math.min
/**
@@ -37,24 +36,23 @@ import kotlin.math.min
* @author Akash Yadav
*/
class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(editor) {
private var mListView: ListView? = null
private var mAdapter: EditorCompletionAdapter? = null
private val mItems: MutableList<CompletionItem> = mutableListOf()
private var listView: ListView? = null
private val items: MutableList<CompletionItem> = mutableListOf()
private val log = ILogger.newInstance(javaClass.simpleName)
init {
mAdapter = super.adapter
setLayout(EditorCompletionLayout())
}
override fun setLayout(layout: CompletionLayout) {
super.setLayout(layout)
(layout.completionList as? ListView)?.let {
mListView = it
it.adapter = mAdapter
listView = it
it.adapter = this.adapter
it.setOnItemLongClickListener { _, view, position, _ ->
val data =
(mItems[position] as? com.itsaky.androidide.lsp.models.CompletionItem)?.data
(items[position] as? com.itsaky.androidide.lsp.models.CompletionItem)?.data
?: return@setOnItemLongClickListener false
val url =
DocumentationReferenceProvider.getUrl(data) ?: return@setOnItemLongClickListener false
@@ -68,17 +66,9 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
}
}
}
override fun setAdapter(adapter: EditorCompletionAdapter?) {
super.setAdapter(adapter)
mAdapter = adapter
mAdapter!!.attachValues(this, mItems)
mAdapter!!.notifyDataSetInvalidated()
mListView!!.adapter = adapter
}
override fun select(pos: Int): Boolean {
if (pos > mAdapter!!.count) {
if (pos > adapter!!.count) {
return false
}
return try {
@@ -88,7 +78,7 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
false
}
}
override fun select(): Boolean {
return try {
super.select()
@@ -97,7 +87,7 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
false
}
}
override fun cancelCompletion() {
if (completionThread != null) {
ProgressManager.instance.cancel(completionThread)
@@ -105,54 +95,71 @@ class EditorCompletionWindow(val editor: IDEEditor) : EditorAutoCompletion(edito
super.cancelCompletion()
popup.dismiss()
}
override fun requireCompletion() {
if (cancelShowUp || !isEnabled) {
return
}
val text = editor.text
if (text.cursor.isSelected) {
if (text.cursor.isSelected || checkNoCompletion()) {
hide()
return
}
if (System.nanoTime() - requestTime < editor.props.cancelCompletionNs) {
hide()
requestTime = System.nanoTime()
return
}
cancelCompletion()
requestTime = System.nanoTime()
super.currentSelection = -1
val reference = AtomicReference<List<CompletionItem>>()
val publisher =
currentSelection = -1
publisher =
CompletionPublisher(
editor.handler,
{
val newItems = reference.get()
mItems.clear()
mItems.addAll(newItems)
mAdapter!!.notifyDataSetChanged()
val newHeight = (mAdapter!!.itemHeight * mAdapter!!.count).toFloat()
val items = publisher.items
this.items.apply {
clear()
addAll(items)
}
if (lastAttachedItems == null || lastAttachedItems.get() != items) {
adapter.attachValues(this, this.items)
adapter.notifyDataSetInvalidated()
lastAttachedItems = WeakReference(items)
} else {
adapter.notifyDataSetChanged()
}
val newHeight = (adapter!!.itemHeight * adapter!!.count).toFloat()
if (newHeight == 0F) {
hide()
}
setSize(width, min(newHeight, maxHeight.toFloat()).toInt())
if (!popup.isShowing) {
dismiss()
show()
}
if (mAdapter!!.count >= 1) {
super.currentSelection = 0
if (adapter!!.count >= 1) {
currentSelection = 0
}
},
editor.editorLanguage.interruptionLevel
)
publisher.setUpdateThreshold(1)
reference.set(publisher.items)
completionThread = CompletionThread(requestTime, publisher)
completionThread.name = "CompletionThread-$requestTime"
setLoading(true)
completionThread.start()
}
}
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
@@ -22,7 +21,6 @@
android:id="@+id/relativeLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimaryContainer"
android:gravity="center_vertical"
android:minHeight="40dp"
tools:ignore="SmallSp">
@@ -44,10 +42,11 @@
<TextView
android:id="@+id/completion_label"
android:textStyle="bold"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="2dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:ellipsize="end"
android:gravity="center_vertical"
@@ -55,7 +54,6 @@
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:textColor="?attr/colorOnPrimaryContainer"
android:textSize="13sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/completion_type"
app:layout_constraintStart_toEndOf="@id/completion_iconText"
app:layout_constraintTop_toTopOf="parent"
@@ -81,9 +79,9 @@
android:id="@+id/completion_api_info"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="2dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:singleLine="true"
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<dimen name="completion_window_corner_radius">8dp</dimen>
<dimen name="completion_window_stroke_width">1dp</dimen>
</resources>