|  |  |  | @ -1,5 +1,6 @@ | 
			
		
	
		
			
				
					|  |  |  |  | package io.legado.app.ui.widget.code | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | import android.annotation.SuppressLint | 
			
		
	
		
			
				
					|  |  |  |  | import android.content.Context | 
			
		
	
		
			
				
					|  |  |  |  | import android.graphics.Canvas | 
			
		
	
		
			
				
					|  |  |  |  | import android.graphics.Paint | 
			
		
	
	
		
			
				
					|  |  |  | @ -12,6 +13,7 @@ import android.text.style.BackgroundColorSpan | 
			
		
	
		
			
				
					|  |  |  |  | import android.text.style.ForegroundColorSpan | 
			
		
	
		
			
				
					|  |  |  |  | import android.text.style.ReplacementSpan | 
			
		
	
		
			
				
					|  |  |  |  | import android.util.AttributeSet | 
			
		
	
		
			
				
					|  |  |  |  | import android.view.MotionEvent | 
			
		
	
		
			
				
					|  |  |  |  | import androidx.annotation.ColorInt | 
			
		
	
		
			
				
					|  |  |  |  | import androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView | 
			
		
	
		
			
				
					|  |  |  |  | import java.util.* | 
			
		
	
	
		
			
				
					|  |  |  | @ -37,6 +39,12 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? | 
			
		
	
		
			
				
					|  |  |  |  |     private val mSyntaxPatternMap: MutableMap<Pattern, Int> = HashMap() | 
			
		
	
		
			
				
					|  |  |  |  |     private var mIndentCharacterList = mutableListOf('{', '+', '-', '*', '/', '=') | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     //滑动距离的最大边界 | 
			
		
	
		
			
				
					|  |  |  |  |     private var mOffsetHeight = 0 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     //是否到顶或者到底的标志 | 
			
		
	
		
			
				
					|  |  |  |  |     private var mBottomFlag = false | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private val mUpdateRunnable = Runnable { | 
			
		
	
		
			
				
					|  |  |  |  |         val source = text | 
			
		
	
		
			
				
					|  |  |  |  |         highlightWithoutChange(source) | 
			
		
	
	
		
			
				
					|  |  |  | @ -102,6 +110,83 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? | 
			
		
	
		
			
				
					|  |  |  |  |         addTextChangedListener(mEditorTextWatcher) | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { | 
			
		
	
		
			
				
					|  |  |  |  |         super.onMeasure(widthMeasureSpec, heightMeasureSpec) | 
			
		
	
		
			
				
					|  |  |  |  |         initOffsetHeight() | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     override fun onTextChanged( | 
			
		
	
		
			
				
					|  |  |  |  |         text: CharSequence, | 
			
		
	
		
			
				
					|  |  |  |  |         start: Int, | 
			
		
	
		
			
				
					|  |  |  |  |         lengthBefore: Int, | 
			
		
	
		
			
				
					|  |  |  |  |         lengthAfter: Int | 
			
		
	
		
			
				
					|  |  |  |  |     ) { | 
			
		
	
		
			
				
					|  |  |  |  |         super.onTextChanged(text, start, lengthBefore, lengthAfter) | 
			
		
	
		
			
				
					|  |  |  |  |         initOffsetHeight() | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     override fun dispatchTouchEvent(event: MotionEvent): Boolean { | 
			
		
	
		
			
				
					|  |  |  |  |         if (event.action == MotionEvent.ACTION_DOWN) { | 
			
		
	
		
			
				
					|  |  |  |  |             //如果是新的按下事件,则对mBottomFlag重新初始化 | 
			
		
	
		
			
				
					|  |  |  |  |             mBottomFlag = mOffsetHeight <= 0 | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         return super.dispatchTouchEvent(event) | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     @SuppressLint("ClickableViewAccessibility") | 
			
		
	
		
			
				
					|  |  |  |  |     override fun onTouchEvent(event: MotionEvent): Boolean { | 
			
		
	
		
			
				
					|  |  |  |  |         val result = super.onTouchEvent(event) | 
			
		
	
		
			
				
					|  |  |  |  |         //如果是需要拦截,则再拦截,这个方法会在onScrollChanged方法之后再调用一次 | 
			
		
	
		
			
				
					|  |  |  |  |         if (!mBottomFlag) parent.requestDisallowInterceptTouchEvent(true) | 
			
		
	
		
			
				
					|  |  |  |  |         return result | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     override fun onScrollChanged(horiz: Int, vert: Int, oldHoriz: Int, oldVert: Int) { | 
			
		
	
		
			
				
					|  |  |  |  |         super.onScrollChanged(horiz, vert, oldHoriz, oldVert) | 
			
		
	
		
			
				
					|  |  |  |  |         if (vert == mOffsetHeight || vert == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |             //这里触发父布局或祖父布局的滑动事件 | 
			
		
	
		
			
				
					|  |  |  |  |             parent.requestDisallowInterceptTouchEvent(false) | 
			
		
	
		
			
				
					|  |  |  |  |             mBottomFlag = true | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     override fun showDropDown() { | 
			
		
	
		
			
				
					|  |  |  |  |         val screenPoint = IntArray(2) | 
			
		
	
		
			
				
					|  |  |  |  |         getLocationOnScreen(screenPoint) | 
			
		
	
		
			
				
					|  |  |  |  |         val displayFrame = Rect() | 
			
		
	
		
			
				
					|  |  |  |  |         getWindowVisibleDisplayFrame(displayFrame) | 
			
		
	
		
			
				
					|  |  |  |  |         val position = selectionStart | 
			
		
	
		
			
				
					|  |  |  |  |         val layout = layout | 
			
		
	
		
			
				
					|  |  |  |  |         val line = layout.getLineForOffset(position) | 
			
		
	
		
			
				
					|  |  |  |  |         val verticalDistanceInDp = (750 + 140 * line) / displayDensity | 
			
		
	
		
			
				
					|  |  |  |  |         dropDownVerticalOffset = verticalDistanceInDp.toInt() | 
			
		
	
		
			
				
					|  |  |  |  |         val horizontalDistanceInDp = layout.getPrimaryHorizontal(position) / displayDensity | 
			
		
	
		
			
				
					|  |  |  |  |         dropDownHorizontalOffset = horizontalDistanceInDp.toInt() | 
			
		
	
		
			
				
					|  |  |  |  |         super.showDropDown() | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private fun initOffsetHeight() { | 
			
		
	
		
			
				
					|  |  |  |  |         val mLayoutHeight: Int | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         //获得内容面板 | 
			
		
	
		
			
				
					|  |  |  |  |         val mLayout = layout ?: return | 
			
		
	
		
			
				
					|  |  |  |  |         //获得内容面板的高度 | 
			
		
	
		
			
				
					|  |  |  |  |         mLayoutHeight = mLayout.height | 
			
		
	
		
			
				
					|  |  |  |  |         //获取上内边距 | 
			
		
	
		
			
				
					|  |  |  |  |         val paddingTop: Int = totalPaddingTop | 
			
		
	
		
			
				
					|  |  |  |  |         //获取下内边距 | 
			
		
	
		
			
				
					|  |  |  |  |         val paddingBottom: Int = totalPaddingBottom | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         //获得控件的实际高度 | 
			
		
	
		
			
				
					|  |  |  |  |         val mHeight: Int = measuredHeight | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         //计算滑动距离的边界 | 
			
		
	
		
			
				
					|  |  |  |  |         mOffsetHeight = mLayoutHeight + paddingTop + paddingBottom - mHeight | 
			
		
	
		
			
				
					|  |  |  |  |         if (mOffsetHeight <= 0) { | 
			
		
	
		
			
				
					|  |  |  |  |             scrollTo(0, 0) | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private fun autoIndent( | 
			
		
	
		
			
				
					|  |  |  |  |         source: CharSequence, | 
			
		
	
		
			
				
					|  |  |  |  |         dest: Spanned, | 
			
		
	
	
		
			
				
					|  |  |  | @ -365,21 +450,6 @@ class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? | 
			
		
	
		
			
				
					|  |  |  |  |         highlightWhileTextChanging = updateWhileTextChanging | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     override fun showDropDown() { | 
			
		
	
		
			
				
					|  |  |  |  |         val screenPoint = IntArray(2) | 
			
		
	
		
			
				
					|  |  |  |  |         getLocationOnScreen(screenPoint) | 
			
		
	
		
			
				
					|  |  |  |  |         val displayFrame = Rect() | 
			
		
	
		
			
				
					|  |  |  |  |         getWindowVisibleDisplayFrame(displayFrame) | 
			
		
	
		
			
				
					|  |  |  |  |         val position = selectionStart | 
			
		
	
		
			
				
					|  |  |  |  |         val layout = layout | 
			
		
	
		
			
				
					|  |  |  |  |         val line = layout.getLineForOffset(position) | 
			
		
	
		
			
				
					|  |  |  |  |         val verticalDistanceInDp = (750 + 140 * line) / displayDensity | 
			
		
	
		
			
				
					|  |  |  |  |         dropDownVerticalOffset = verticalDistanceInDp.toInt() | 
			
		
	
		
			
				
					|  |  |  |  |         val horizontalDistanceInDp = layout.getPrimaryHorizontal(position) / displayDensity | 
			
		
	
		
			
				
					|  |  |  |  |         dropDownHorizontalOffset = horizontalDistanceInDp.toInt() | 
			
		
	
		
			
				
					|  |  |  |  |         super.showDropDown() | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private inner class TabWidthSpan : ReplacementSpan() { | 
			
		
	
		
			
				
					|  |  |  |  |         override fun getSize( | 
			
		
	
		
			
				
					|  |  |  |  |             paint: Paint, | 
			
		
	
	
		
			
				
					|  |  |  | 
 |