diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt index f8f6d0c..6d5a1ed 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt @@ -18,7 +18,7 @@ import io.github.kbiakov.codeview.highlight.color /** * @class CodeView * - * View for showing code content with syntax highlighting. + * Display code with syntax highlighting. * * @author Kirill Biakov */ @@ -28,8 +28,9 @@ class CodeView @JvmOverloads constructor( defStyleAttr: Int = 0 ) : RelativeLayout(context, attrs, defStyleAttr) { - private val vCodeList: RecyclerView - private val vShadows: Map + private val rvContent: RecyclerView + private val shadows: Map + private val adapter get() = rvContent.adapter as? AbstractCodeAdapter<*> /** * Primary constructor. @@ -38,12 +39,12 @@ class CodeView @JvmOverloads constructor( inflate(context, R.layout.layout_code_view, this) attrs?.let(::checkStartAnimation) - vCodeList = findViewById(R.id.rv_code_content).apply { + rvContent = findViewById(R.id.rv_content).apply { layoutManager = LinearLayoutManager(context) isNestedScrollingEnabled = true } - vShadows = mapOf( + shadows = mapOf( ShadowPosition.RightBorder to R.id.shadow_right_border, ShadowPosition.NumBottom to R.id.shadow_num_bottom, ShadowPosition.ContentBottom to R.id.shadow_content_bottom @@ -78,39 +79,27 @@ class CodeView @JvmOverloads constructor( } } - /** - * Highlight code with defined programming language. - * It holds the placeholder on view until code is not highlighted. - */ - private fun highlight() { - getAdapter()?.apply { - highlight { - checkHighlightAnimation(::notifyDataSetChanged) - } - } - } - /** * Border shadows will shown if full listing presented. - * It helps to see what part of code is scrolled & hidden. + * It helps to see which part of code is scrolled & hidden. * * @param isVisible Is shadows visible */ fun setupShadows(isVisible: Boolean) { val visibility = if (isVisible) VISIBLE else GONE - val theme = getOptionsOrDefault().theme - vShadows.forEach { (pos, view) -> + val theme = optionsOrDefault.theme + shadows.forEach { (pos, view) -> view.visibility = visibility view.setSafeBackground(pos.createShadow(theme)) } } - // - Initialization + // - Options /** - * Prepare view with default adapter & options. + * View options accessor. */ - private fun prepare() = setAdapter(CodeWithNotesAdapter(context)) + private val optionsOrDefault get() = adapter?.options ?: Options(context) /** * Initialize with options. @@ -119,102 +108,98 @@ class CodeView @JvmOverloads constructor( */ fun setOptions(options: Options) = setAdapter(CodeWithNotesAdapter(context, options)) - /** - * Initialize with adapter. - * - * @param adapter Adapter - */ - fun setAdapter(adapter: AbstractCodeAdapter<*>) { - vCodeList.adapter = adapter - highlight() - } - - // - Options - - /** - * View options accessor. - */ - fun getOptions() = getAdapter()?.options - fun getOptionsOrDefault() = getOptions() ?: Options(context) - /** * Update options or initialize if needed. * * @param options Options */ fun updateOptions(options: Options) { - getAdapter() ?: setOptions(options) - getAdapter()?.options = options + adapter + ?.let { it.options = options } + ?: setOptions(options) + setupShadows(options.shadows) } - fun updateOptions(body: Options.() -> Unit) { - val options = getOptions() ?: getOptionsOrDefault() - updateOptions(options.apply(body)) - } + /** + * Update options or initialize if needed. + * + * @param body Options mutator + */ + fun updateOptions(body: Options.() -> Unit) = + optionsOrDefault + .apply(body) + .apply(::updateOptions) // - Adapter /** - * Code adapter accessor. + * Initialize with adapter. + * + * Highlight code with defined programming language. + * It holds the placeholder on view until code is not highlighted. + * + * @param adapter Adapter */ - fun getAdapter() = vCodeList.adapter as? AbstractCodeAdapter<*> + fun setAdapter(adapter: AbstractCodeAdapter<*>) { + rvContent.adapter = adapter.apply { + highlight { checkHighlightAnimation(::notifyDataSetChanged) } + } + } /** * Update adapter or initialize if needed. * * @param adapter Adapter + * @param isUseCurrent Use options that are already set or default */ - fun updateAdapter(adapter: AbstractCodeAdapter<*>) { - adapter.options = getOptionsOrDefault() - setAdapter(adapter) + fun updateAdapter(adapter: AbstractCodeAdapter<*>, isUseCurrent: Boolean) { + setAdapter(adapter.apply { + if (isUseCurrent) { + options = optionsOrDefault + } + }) } // - Set code /** - * Set code content. - * - * There are two ways before code will be highlighted: - * 1) view is not initialized (adapter or options are not set), + * Set code content. View is: + * 1) not initialized (adapter or options is not set): * prepare with default params & try to classify language - * 2) view initialized with some params, language: - * a) is set: used defined programming language + * 2) initialized (with some params), language is: + * a) set: use defined * b) not set: try to classify * * @param code Code content */ - fun setCode(code: String) { - getAdapter() ?: prepare() - getAdapter()?.updateCode(code) - } + fun setCode(code: String) = setCode(code, null) /** - * Set code content. - * - * There are two ways before code will be highlighted: - * 1) view is not initialized, prepare with default params - * 2) view initialized with some params, set new language + * Set code content. View is: + * 1) not initialized: prepare with default params + * 2) initialized (with some params): set new language * * @param code Code content * @param language Programming language */ - fun setCode(code: String, language: String) { - val options = getOptionsOrDefault() - updateOptions(options.withLanguage(language)) - getAdapter()?.updateCode(code) + fun setCode(code: String, language: String? = null) { + val options = optionsOrDefault.apply { + this.language = language + } + (adapter ?: CodeWithNotesAdapter(context, options) + .apply(::setAdapter)) + .updateCode(code) } companion object { - private fun AttributeSet.isAnimateOnStart(context: Context): Boolean { - context.theme.obtainStyledAttributes(this, R.styleable.CodeView, 0, 0).apply { - val flag = getBoolean(R.styleable.CodeView_animateOnStart, false) - recycle() - return@isAnimateOnStart flag - } - return false - } + private fun AttributeSet.isAnimateOnStart(context: Context) = + context.theme.obtainStyledAttributes(this, R.styleable.CodeView, 0, 0).run { + val isAnimate = getBoolean(R.styleable.CodeView_animateOnStart, false) + recycle() + isAnimate + } private fun View.setSafeBackground(newBackground: Drawable) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { diff --git a/codeview/src/main/res/layout/layout_code_view.xml b/codeview/src/main/res/layout/layout_code_view.xml index ab0491f..3f3d70e 100644 --- a/codeview/src/main/res/layout/layout_code_view.xml +++ b/codeview/src/main/res/layout/layout_code_view.xml @@ -12,7 +12,7 @@ android:fillViewport="true">