From 76cba7327b71da2406c29d060b5a4279cdbb7b18 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Wed, 6 Sep 2023 13:10:21 +0530 Subject: [PATCH] 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 --- .../editor/adapters/CompletionListAdapter.kt | 68 +++++++------- .../schemes/internal/parser/SchemeParser.kt | 4 + .../editor/ui/EditorCompletionLayout.kt | 31 +++++-- .../editor/ui/EditorCompletionWindow.kt | 89 ++++++++++--------- .../res/layout/layout_completion_item.xml | 10 +-- editor/src/main/res/values/dimens.xml | 21 +++++ 6 files changed, 132 insertions(+), 91 deletions(-) create mode 100644 editor/src/main/res/values/dimens.xml diff --git a/editor/src/main/java/com/itsaky/androidide/editor/adapters/CompletionListAdapter.kt b/editor/src/main/java/com/itsaky/androidide/editor/adapters/CompletionListAdapter.kt index 676a490ec..5316950a7 100755 --- a/editor/src/main/java/com/itsaky/androidide/editor/adapters/CompletionListAdapter.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/adapters/CompletionListAdapter.kt @@ -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) { - 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) { diff --git a/editor/src/main/java/com/itsaky/androidide/editor/schemes/internal/parser/SchemeParser.kt b/editor/src/main/java/com/itsaky/androidide/editor/schemes/internal/parser/SchemeParser.kt index db66c5659..49618a18d 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/schemes/internal/parser/SchemeParser.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/schemes/internal/parser/SchemeParser.kt @@ -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", diff --git a/editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionLayout.kt b/editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionLayout.kt index c58498a4f..65f9d6448 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionLayout.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionLayout.kt @@ -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 { + marginStart = strokeWidth + topMargin = strokeWidth + marginEnd = strokeWidth + bottomMargin = strokeWidth + } + } } } diff --git a/editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionWindow.kt b/editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionWindow.kt index 153c94dce..79b18a89b 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionWindow.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionWindow.kt @@ -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 = mutableListOf() + + private var listView: ListView? = null + private val items: MutableList = 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>() - 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() } - + } diff --git a/editor/src/main/res/layout/layout_completion_item.xml b/editor/src/main/res/layout/layout_completion_item.xml index d08982a0e..2a7db4c24 100755 --- a/editor/src/main/res/layout/layout_completion_item.xml +++ b/editor/src/main/res/layout/layout_completion_item.xml @@ -1,5 +1,4 @@ - - + + + 8dp + 1dp + \ No newline at end of file