mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Editor | Feature | Code block, design and clicks (#2231)
* code block update * code block, holder design * block list extension + tests * only background style menu for text blocks * code block fixes * update gradle * rename fun * back android 7 fix Co-authored-by: konstantiniiv <ki@anytype.io>
This commit is contained in:
parent
1eeb6a0067
commit
9eeea1e722
7 changed files with 176 additions and 64 deletions
|
@ -22,7 +22,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.1'
|
||||
classpath 'com.android.tools.build:gradle:7.1.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
|
|
|
@ -271,4 +271,9 @@ fun Document.updateTextContent(
|
|||
fun List<Block>.getChildrenIdsList(parent: Id): List<String> {
|
||||
val root = this.firstOrNull { it.id == parent }
|
||||
return root?.children ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun List<Block>.isAllTextAndNoneCodeBlocks(): Boolean =
|
||||
all { block ->
|
||||
block.content is Content.Text && block.content.style != Content.Text.Style.CODE_SNIPPET
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
package com.anytypeio.anytype.core_ui.features.editor.holders.other
|
||||
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION_CODES.N
|
||||
import android.os.Build.VERSION_CODES.N_MR1
|
||||
import android.text.Editable
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockCodeSnippetBinding
|
||||
import com.anytypeio.anytype.core_ui.extensions.lighter
|
||||
|
@ -38,15 +37,13 @@ class Code(val binding: ItemBlockCodeSnippetBinding) : BlockViewHolder(binding.r
|
|||
get() = itemView
|
||||
val content: CodeTextInputWidget
|
||||
get() = binding.snippet
|
||||
val container: LinearLayout
|
||||
get() = binding.snippetContainer
|
||||
|
||||
val editorTouchProcessor = EditorTouchProcessor(
|
||||
fallback = { e -> itemView.onTouchEvent(e) }
|
||||
)
|
||||
|
||||
init {
|
||||
itemView.setOnTouchListener { v, e -> editorTouchProcessor.process(v, e) }
|
||||
content.setOnTouchListener { v, e -> editorTouchProcessor.process(v, e) }
|
||||
}
|
||||
|
||||
fun bind(
|
||||
|
@ -97,7 +94,7 @@ class Code(val binding: ItemBlockCodeSnippetBinding) : BlockViewHolder(binding.r
|
|||
content.selectionWatcher = { onSelectionChanged(item.id, it) }
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
content.setOnClickListener {
|
||||
onTextInputClicked(item.id)
|
||||
if (Build.VERSION.SDK_INT == N || Build.VERSION.SDK_INT == N_MR1) {
|
||||
content.context.imm().showSoftInput(content, InputMethodManager.SHOW_FORCED)
|
||||
|
@ -118,11 +115,8 @@ class Code(val binding: ItemBlockCodeSnippetBinding) : BlockViewHolder(binding.r
|
|||
}
|
||||
|
||||
fun indentize(item: BlockView.Indentable) {
|
||||
binding.snippetContainer.updateLayoutParams<FrameLayout.LayoutParams> {
|
||||
apply {
|
||||
val extra = item.indent * dimen(R.dimen.indent)
|
||||
leftMargin = 0 + extra
|
||||
}
|
||||
root.updateLayoutParams<RecyclerView.LayoutParams> {
|
||||
leftMargin = item.indent * dimen(R.dimen.indent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +168,7 @@ class Code(val binding: ItemBlockCodeSnippetBinding) : BlockViewHolder(binding.r
|
|||
}
|
||||
|
||||
fun select(item: BlockView.Selectable) {
|
||||
root.isSelected = item.isSelected
|
||||
binding.selected.isSelected = item.isSelected
|
||||
}
|
||||
|
||||
fun setFocus(item: Focusable) {
|
||||
|
@ -206,11 +200,11 @@ class Code(val binding: ItemBlockCodeSnippetBinding) : BlockViewHolder(binding.r
|
|||
Timber.d("Setting background color: $color")
|
||||
val value = ThemeColor.values().find { value -> value.title == color }
|
||||
if (value != null && value != ThemeColor.DEFAULT) {
|
||||
(container.background as? GradientDrawable)?.setColor(root.resources.lighter(value, 0))
|
||||
(root.background as? ColorDrawable)?.color = root.resources.lighter(value, 0)
|
||||
} else {
|
||||
val defaultBackgroundColor =
|
||||
content.context.resources.getColor(R.color.shape_tertiary, null)
|
||||
(container.background as? GradientDrawable)?.setColor(defaultBackgroundColor)
|
||||
(root.background as? ColorDrawable)?.color = defaultBackgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,58 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/root"
|
||||
style="@style/DefaultCodeBlockRootStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="@drawable/item_block_code_multi_select_mode_selector"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="6dp">
|
||||
android:background="@color/shape_tertiary">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/snippetContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="@drawable/item_block_code_multi_select_unselected"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp">
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/code_menu"
|
||||
style="@style/BlockCodeLanguageMenuStyle"
|
||||
android:id="@+id/codeMenu"
|
||||
style="@style/DefaultCodeBlockLanguageMenuStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp_12"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="13dp"
|
||||
android:text="@string/block_code_menu_title" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:fillViewport="true"
|
||||
android:scrollbars="none"
|
||||
android:overScrollMode="never"
|
||||
android:id="@+id/scroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none">
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.text.CodeTextInputWidget
|
||||
android:id="@+id/snippet"
|
||||
style="@style/BlockCodeContentStyle"
|
||||
style="@style/DefaultCodeBlockContentStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
android:paddingTop="23dp"
|
||||
android:paddingBottom="30dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:focusable="true"
|
||||
android:paddingTop="18dp"
|
||||
android:paddingEnd="32dp"
|
||||
android:paddingBottom="21dp"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="@string/default_text_placeholder" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
</HorizontalScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/selected"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:layout_marginEnd="@dimen/dp_8"
|
||||
android:background="@drawable/item_block_multi_select_mode_selector"
|
||||
tools:background="@drawable/item_block_multi_select_selected" />
|
||||
</FrameLayout>
|
|
@ -148,23 +148,6 @@
|
|||
<item name="android:paddingBottom">4dp</item>
|
||||
</style>
|
||||
|
||||
<style name="BlockCodeContentStyle">
|
||||
<item name="android:inputType">textMultiLine|textNoSuggestions</item>
|
||||
<item name="android:background">@null</item>
|
||||
<item name="android:lineSpacingExtra">7sp</item>
|
||||
<item name="android:textColor">@color/text_primary</item>
|
||||
<item name="android:textSize">15sp</item>
|
||||
</style>
|
||||
|
||||
<style name="BlockCodeLanguageMenuStyle">
|
||||
<item name="android:fontFamily">@font/inter_regular</item>
|
||||
<item name="android:lineSpacingExtra">7sp</item>
|
||||
<item name="android:drawablePadding">4dp</item>
|
||||
<item name="android:drawableEnd">@drawable/ic_turn_into_arrow</item>
|
||||
<item name="android:textColor">@color/text_secondary</item>
|
||||
<item name="android:textSize">15sp</item>
|
||||
</style>
|
||||
|
||||
<style name="BlockBookmarkTitleStyle">
|
||||
<item name="android:fontFamily">@font/inter_medium</item>
|
||||
<item name="android:textSize">13sp</item>
|
||||
|
@ -1013,4 +996,28 @@
|
|||
<item name="android:textSize">15sp</item>
|
||||
</style>
|
||||
|
||||
<!-- Editor, Code block -->
|
||||
<style name="DefaultCodeBlockRootStyle">
|
||||
<item name="android:layout_marginTop">@dimen/dp_10</item>
|
||||
<item name="android:layout_marginBottom">@dimen/dp_10</item>
|
||||
</style>
|
||||
|
||||
<style name="DefaultCodeBlockContentStyle">
|
||||
<item name="android:inputType">textMultiLine|textNoSuggestions</item>
|
||||
<item name="android:background">@null</item>
|
||||
<item name="android:lineSpacingExtra">7sp</item>
|
||||
<item name="android:textColor">@color/text_primary</item>
|
||||
<item name="android:textSize">15sp</item>
|
||||
<item name="android:fontFamily">monospace</item>
|
||||
</style>
|
||||
|
||||
<style name="DefaultCodeBlockLanguageMenuStyle">
|
||||
<item name="android:fontFamily">@font/inter_regular</item>
|
||||
<item name="android:lineSpacingExtra">7sp</item>
|
||||
<item name="android:drawablePadding">4dp</item>
|
||||
<item name="android:drawableEnd">@drawable/ic_turn_into_arrow</item>
|
||||
<item name="android:textColor">@color/text_secondary</item>
|
||||
<item name="android:textSize">15sp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -5,7 +5,9 @@ import com.anytypeio.anytype.domain.common.MockDataFactory
|
|||
import com.anytypeio.anytype.core_models.ext.*
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class BlockExtensionTest {
|
||||
|
||||
|
@ -1015,4 +1017,109 @@ class BlockExtensionTest {
|
|||
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return true on all non code and texted blocks`() {
|
||||
|
||||
val root = MockDataFactory.randomUuid()
|
||||
|
||||
val a = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields.empty(),
|
||||
children = emptyList(),
|
||||
content = Block.Content.Text(
|
||||
text = MockDataFactory.randomString(),
|
||||
marks = emptyList(),
|
||||
style = Block.Content.Text.Style.NUMBERED
|
||||
)
|
||||
)
|
||||
|
||||
val b = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields.empty(),
|
||||
children = emptyList(),
|
||||
content = Block.Content.Text(
|
||||
text = MockDataFactory.randomString(),
|
||||
marks = emptyList(),
|
||||
style = (Block.Content.Text.Style.P)
|
||||
)
|
||||
)
|
||||
|
||||
val document = listOf(a, b)
|
||||
|
||||
val result = document.isAllTextAndNoneCodeBlocks()
|
||||
|
||||
assertTrue(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return false on texted blocks and code block`() {
|
||||
|
||||
val root = MockDataFactory.randomUuid()
|
||||
|
||||
val a = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields.empty(),
|
||||
children = emptyList(),
|
||||
content = Block.Content.Text(
|
||||
text = MockDataFactory.randomString(),
|
||||
marks = emptyList(),
|
||||
style = Block.Content.Text.Style.NUMBERED
|
||||
)
|
||||
)
|
||||
|
||||
val b = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields.empty(),
|
||||
children = emptyList(),
|
||||
content = Block.Content.Text(
|
||||
text = MockDataFactory.randomString(),
|
||||
marks = emptyList(),
|
||||
style = (Block.Content.Text.Style.P)
|
||||
)
|
||||
)
|
||||
|
||||
val c = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields.empty(),
|
||||
children = emptyList(),
|
||||
content = Block.Content.Text(
|
||||
text = MockDataFactory.randomString(),
|
||||
marks = emptyList(),
|
||||
style = (Block.Content.Text.Style.CODE_SNIPPET)
|
||||
)
|
||||
)
|
||||
|
||||
val document = listOf(a, b, c)
|
||||
|
||||
val result = document.isAllTextAndNoneCodeBlocks()
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return false on media blocks`() {
|
||||
|
||||
val root = MockDataFactory.randomUuid()
|
||||
|
||||
val a = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields.empty(),
|
||||
children = emptyList(),
|
||||
content = Block.Content.File()
|
||||
)
|
||||
|
||||
val b = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields.empty(),
|
||||
children = emptyList(),
|
||||
content = Block.Content.File()
|
||||
)
|
||||
|
||||
val document = listOf(a, b)
|
||||
|
||||
val result = document.isAllTextAndNoneCodeBlocks()
|
||||
|
||||
assertFalse(result)
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import com.anytypeio.anytype.core_models.ext.asMap
|
|||
import com.anytypeio.anytype.core_models.ext.content
|
||||
import com.anytypeio.anytype.core_models.ext.descendants
|
||||
import com.anytypeio.anytype.core_models.ext.getFirstLinkOrObjectMarkupParam
|
||||
import com.anytypeio.anytype.core_models.ext.isAllTextAndNoneCodeBlocks
|
||||
import com.anytypeio.anytype.core_models.ext.parents
|
||||
import com.anytypeio.anytype.core_models.ext.process
|
||||
import com.anytypeio.anytype.core_models.ext.sortByType
|
||||
|
@ -2339,7 +2340,7 @@ class EditorViewModel(
|
|||
|
||||
private fun proceedWithMultiStyleToolbarEvent() {
|
||||
val selected = blocks.filter { currentSelection().contains(it.id) }
|
||||
val isAllSelectedText = selected.all { it.content is Content.Text }
|
||||
val isAllSelectedText = selected.isAllTextAndNoneCodeBlocks()
|
||||
mode = EditorMode.Styling.Multi(currentSelection())
|
||||
if (isAllSelectedText) {
|
||||
controlPanelInteractor.onEvent(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue