mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-445 Editor | Simple tables, dynamic cell height integration (#2626)
* DROID-445 feature toggle added * DROID-445 custom grid layout manager as container * DROID-445 split get span count * DROID-445 editable cells adapter + holder * DROID-445 selection and focus listeners * DROID-445 show cell menu on cell click * DROID-445 editable cell adapter added * DROID-445 editor, cell clicks + focus * DROID-445 fix adapter update * DROID-445 after develop merge fixes * DROID-445 fixes * DROID-445 select cell on focus change * DROID-445 test update * DROID-445 turn off editable cells * DROID-445 ci * DROID-445 turn on editing cell * DROID-445 fix test * DROID-445 ci off Co-authored-by: konstantiniiv <ki@anytype.io>
This commit is contained in:
parent
16293155e0
commit
f97fa3ab4c
13 changed files with 504 additions and 68 deletions
|
@ -744,7 +744,12 @@ class BlockAdapter(
|
|||
)
|
||||
HOLDER_TABLE -> TableBlockHolder(
|
||||
ItemBlockTableBinding.inflate(inflater, parent, false),
|
||||
clickListener = onClickListener
|
||||
clickListener = onClickListener,
|
||||
onTextBlockTextChanged = onTextBlockTextChanged,
|
||||
onMentionEvent = onMentionEvent,
|
||||
onSlashEvent = onSlashEvent,
|
||||
onSelectionChanged = onSelectionChanged,
|
||||
onFocusChanged = onFocusChanged
|
||||
)
|
||||
else -> throw IllegalStateException("Unexpected view type: $viewType")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
package com.anytypeio.anytype.core_ui.features.table
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTableRowItemEditableBinding
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTableRowItemEmptyBinding
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTableSpaceBinding
|
||||
import com.anytypeio.anytype.core_ui.features.editor.ItemProviderAdapter
|
||||
import com.anytypeio.anytype.core_ui.features.editor.marks
|
||||
import com.anytypeio.anytype.core_ui.features.editor.withBlock
|
||||
import com.anytypeio.anytype.core_ui.features.table.holders.TableCellHolder
|
||||
import com.anytypeio.anytype.core_ui.features.table.holders.EditableCellHolder
|
||||
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
|
||||
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionEvent
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
|
||||
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
|
||||
|
||||
class TableEditableCellsAdapter(
|
||||
private var items: List<BlockView.Table.Cell>,
|
||||
private val clicked: (ListenerType) -> Unit,
|
||||
private val onTextBlockTextChanged: (BlockView.Text) -> Unit,
|
||||
private val onMentionEvent: (MentionEvent) -> Unit,
|
||||
private val onSlashEvent: (SlashEvent) -> Unit,
|
||||
private val onSelectionChanged: (Id, IntRange) -> Unit,
|
||||
private val onFocusChanged: (Id, Boolean) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
|
||||
ItemProviderAdapter<BlockView.Text.Paragraph?> {
|
||||
|
||||
private var tableBlockId = ""
|
||||
|
||||
fun setTableBlockId(id: Id) {
|
||||
tableBlockId = id
|
||||
}
|
||||
|
||||
override fun provide(pos: Int): BlockView.Text.Paragraph? {
|
||||
return (items[pos] as? BlockView.Table.Cell.Text)?.block
|
||||
}
|
||||
|
||||
fun updateWithDiffUtil(items: List<BlockView.Table.Cell>) {
|
||||
val result = DiffUtil.calculateDiff(TableCellsDiffUtil(old = this.items, new = items))
|
||||
this.items = items
|
||||
result.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
when (viewType) {
|
||||
TYPE_CELL -> {
|
||||
val binding = ItemBlockTableRowItemEditableBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return EditableCellHolder(
|
||||
binding = binding,
|
||||
clicked = clicked
|
||||
).apply {
|
||||
content.setOnClickListener {
|
||||
val pos = bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
val item = items[pos]
|
||||
if (item is BlockView.Table.Cell.Text) {
|
||||
clicked(
|
||||
ListenerType.TableTextCell(
|
||||
tableId = tableBlockId,
|
||||
cellId = item.block.id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
content.setOnLongClickListener { _ ->
|
||||
val pos = bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
clicked(ListenerType.LongClick(tableBlockId))
|
||||
}
|
||||
true
|
||||
}
|
||||
content.selectionWatcher = { selection ->
|
||||
val pos = bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
val item = items[pos]
|
||||
if (item is BlockView.Table.Cell.Text) {
|
||||
item.block.cursor = selection.last
|
||||
onSelectionChanged(item.block.id, selection)
|
||||
}
|
||||
}
|
||||
}
|
||||
content.setOnFocusChangeListener { _, hasFocus ->
|
||||
val pos = bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
val item = items[pos]
|
||||
if (item is BlockView.Table.Cell.Text) {
|
||||
cellSelection(hasFocus)
|
||||
onFocusChanged(item.block.id, hasFocus)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setupViewHolder(
|
||||
onTextChanged = { editable ->
|
||||
this.withBlock<BlockView.Text> { item ->
|
||||
item.apply {
|
||||
text = editable.toString()
|
||||
marks = editable.marks()
|
||||
}
|
||||
onTextBlockTextChanged(item)
|
||||
}
|
||||
},
|
||||
onEmptyBlockBackspaceClicked = {},
|
||||
onSplitLineEnterClicked = { _, _, _ -> },
|
||||
onNonEmptyBlockBackspaceClicked = { _, _ -> },
|
||||
onMentionEvent = onMentionEvent,
|
||||
onSlashEvent = onSlashEvent,
|
||||
onBackPressedCallback = null
|
||||
)
|
||||
}
|
||||
}
|
||||
TYPE_EMPTY -> {
|
||||
val binding = ItemBlockTableRowItemEmptyBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return EmptyHolder(binding = binding).apply {
|
||||
itemView.setOnClickListener {
|
||||
val pos = bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
val item = items[bindingAdapterPosition]
|
||||
if (item is BlockView.Table.Cell.Empty) {
|
||||
clicked(
|
||||
ListenerType.TableEmptyCell(
|
||||
cellId = item.getId(),
|
||||
rowId = item.rowId,
|
||||
tableId = tableBlockId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TYPE_SPACE -> {
|
||||
val binding = ItemBlockTableSpaceBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return TableCellHolder.TableSpaceHolder(binding)
|
||||
}
|
||||
else -> throw UnsupportedOperationException("wrong viewtype:$viewType")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is EditableCellHolder) {
|
||||
holder.bind(
|
||||
item = (items[position] as BlockView.Table.Cell.Text).block
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
override fun getItemViewType(position: Int): Int = when (items[position]) {
|
||||
is BlockView.Table.Cell.Empty -> TYPE_EMPTY
|
||||
is BlockView.Table.Cell.Text -> TYPE_CELL
|
||||
BlockView.Table.Cell.Space -> TYPE_SPACE
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_CELL = 1
|
||||
const val TYPE_SPACE = 2
|
||||
const val TYPE_EMPTY = 3
|
||||
}
|
||||
|
||||
class EmptyHolder(binding: ItemBlockTableRowItemEmptyBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class TableCellsDiffUtil(
|
||||
private val old: List<BlockView.Table.Cell>,
|
||||
private val new: List<BlockView.Table.Cell>
|
||||
) : DiffUtil.Callback() {
|
||||
|
||||
override fun getOldListSize() = old.size
|
||||
override fun getNewListSize() = new.size
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val newItem = new[newItemPosition]
|
||||
val oldItem = old[oldItemPosition]
|
||||
if (newItem is BlockView.Table.Cell.Space && oldItem is BlockView.Table.Cell.Space) {
|
||||
return true
|
||||
}
|
||||
if (newItem is BlockView.Table.Cell.Empty && oldItem is BlockView.Table.Cell.Empty) {
|
||||
return newItem.rowId == oldItem.rowId && newItem.columnId == oldItem.columnId
|
||||
}
|
||||
if (newItem is BlockView.Table.Cell.Text && oldItem is BlockView.Table.Cell.Text) {
|
||||
return newItem.rowId == oldItem.rowId && newItem.columnId == oldItem.columnId
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return old[oldItemPosition] == new[newItemPosition]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package com.anytypeio.anytype.core_ui.features.table.holders
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_ui.BuildConfig
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTableRowItemEditableBinding
|
||||
import com.anytypeio.anytype.core_ui.features.editor.holders.text.Text
|
||||
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
|
||||
import com.anytypeio.anytype.core_utils.ext.dimen
|
||||
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
|
||||
|
||||
class EditableCellHolder(
|
||||
private val binding: ItemBlockTableRowItemEditableBinding,
|
||||
clicked: (ListenerType) -> Unit,
|
||||
) : Text<BlockView.Text.Paragraph>(binding.root, clicked) {
|
||||
|
||||
override val root: View = binding.root
|
||||
override val content: TextInputWidget = binding.textContent
|
||||
private val selectionView: View = binding.selection
|
||||
|
||||
private val mentionIconSize: Int
|
||||
private val mentionIconPadding: Int
|
||||
private val mentionCheckedIcon: Drawable?
|
||||
private val mentionUncheckedIcon: Drawable?
|
||||
private val mentionInitialsSize: Float
|
||||
|
||||
init {
|
||||
setup()
|
||||
with(itemView.context) {
|
||||
mentionIconSize =
|
||||
resources.getDimensionPixelSize(R.dimen.mention_span_image_size_default)
|
||||
mentionIconPadding =
|
||||
resources.getDimensionPixelSize(R.dimen.mention_span_image_padding_default)
|
||||
mentionUncheckedIcon = ContextCompat.getDrawable(this, R.drawable.ic_task_0_text_16)
|
||||
mentionCheckedIcon = ContextCompat.getDrawable(this, R.drawable.ic_task_1_text_16)
|
||||
mentionInitialsSize = resources.getDimension(R.dimen.mention_span_initials_size_default)
|
||||
}
|
||||
applyDefaultOffsets()
|
||||
}
|
||||
|
||||
private fun applyDefaultOffsets() {
|
||||
if (!BuildConfig.NESTED_DECORATION_ENABLED) {
|
||||
binding.root.updatePadding(
|
||||
left = dimen(R.dimen.default_document_item_padding_start),
|
||||
right = dimen(R.dimen.default_document_item_padding_end)
|
||||
)
|
||||
binding.root.updateLayoutParams<RecyclerView.LayoutParams> {
|
||||
topMargin = dimen(R.dimen.default_document_item_margin_top)
|
||||
bottomMargin = dimen(R.dimen.default_document_item_margin_bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cellSelection(isSelected: Boolean) {
|
||||
selectionView.isSelected = isSelected
|
||||
}
|
||||
|
||||
override fun indentize(item: BlockView.Indentable) {}
|
||||
override fun getMentionIconSize(): Int = mentionIconSize
|
||||
override fun getMentionIconPadding(): Int = mentionIconPadding
|
||||
override fun getMentionCheckedIcon(): Drawable? = mentionCheckedIcon
|
||||
override fun getMentionUncheckedIcon(): Drawable? = mentionUncheckedIcon
|
||||
override fun getMentionInitialsSize(): Float = mentionInitialsSize
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
package com.anytypeio.anytype.core_ui.features.table.holders
|
||||
|
||||
import android.widget.FrameLayout
|
||||
import androidx.recyclerview.widget.CustomGridLayoutManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.databinding.ItemBlockTableBinding
|
||||
import com.anytypeio.anytype.core_ui.extensions.drawable
|
||||
|
@ -14,12 +16,21 @@ import com.anytypeio.anytype.core_ui.features.table.TableCellsDiffUtil
|
|||
import com.anytypeio.anytype.core_ui.layout.TableHorizontalItemDivider
|
||||
import com.anytypeio.anytype.core_ui.layout.TableVerticalItemDivider
|
||||
import com.anytypeio.anytype.core_models.ThemeColor
|
||||
import com.anytypeio.anytype.core_ui.features.table.TableEditableCellsAdapter
|
||||
import com.anytypeio.anytype.presentation.BuildConfig
|
||||
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
|
||||
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionEvent
|
||||
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
|
||||
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashEvent
|
||||
|
||||
class TableBlockHolder(
|
||||
binding: ItemBlockTableBinding,
|
||||
clickListener: (ListenerType) -> Unit
|
||||
clickListener: (ListenerType) -> Unit,
|
||||
onTextBlockTextChanged: (BlockView.Text) -> Unit,
|
||||
onMentionEvent: (MentionEvent) -> Unit,
|
||||
onSlashEvent: (SlashEvent) -> Unit,
|
||||
onSelectionChanged: (Id, IntRange) -> Unit,
|
||||
onFocusChanged: (Id, Boolean) -> Unit
|
||||
) : BlockViewHolder(binding.root) {
|
||||
|
||||
val root: FrameLayout = binding.root
|
||||
|
@ -30,15 +41,21 @@ class TableBlockHolder(
|
|||
differ = TableCellsDiffUtil,
|
||||
clickListener = clickListener
|
||||
)
|
||||
private val lm = GridLayoutManager(itemView.context, 1, GridLayoutManager.HORIZONTAL, false)
|
||||
|
||||
private val mSpanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return when (recycler.adapter?.getItemViewType(position)) {
|
||||
TableBlockAdapter.TYPE_CELL -> 1
|
||||
else -> lm.spanCount
|
||||
}
|
||||
}
|
||||
private val tableEditableCellsAdapter = TableEditableCellsAdapter(
|
||||
items = listOf(),
|
||||
clicked = clickListener,
|
||||
onTextBlockTextChanged = onTextBlockTextChanged,
|
||||
onMentionEvent = onMentionEvent,
|
||||
onSlashEvent = onSlashEvent,
|
||||
onSelectionChanged = onSelectionChanged,
|
||||
onFocusChanged = onFocusChanged
|
||||
)
|
||||
|
||||
private val lm = if (BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
CustomGridLayoutManager(itemView.context, 1, GridLayoutManager.HORIZONTAL, false)
|
||||
} else {
|
||||
GridLayoutManager(itemView.context, 1, GridLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -48,8 +65,30 @@ class TableBlockHolder(
|
|||
|
||||
recycler.apply {
|
||||
layoutManager = lm
|
||||
lm.spanSizeLookup = mSpanSizeLookup
|
||||
adapter = tableAdapter
|
||||
if (BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
(lm as CustomGridLayoutManager).spanSizeLookup =
|
||||
object : CustomGridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return when (recycler.adapter?.getItemViewType(position)) {
|
||||
TableEditableCellsAdapter.TYPE_CELL, TableEditableCellsAdapter.TYPE_EMPTY -> 1
|
||||
else -> lm.spanCount
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter = tableEditableCellsAdapter
|
||||
setHasFixedSize(true)
|
||||
} else {
|
||||
(lm as GridLayoutManager).spanSizeLookup =
|
||||
object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return when (recycler.adapter?.getItemViewType(position)) {
|
||||
TableBlockAdapter.TYPE_CELL -> 1
|
||||
else -> lm.spanCount
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter = tableAdapter
|
||||
}
|
||||
addItemDecoration(verticalDecorator)
|
||||
addItemDecoration(horizontalDecorator)
|
||||
}
|
||||
|
@ -57,9 +96,15 @@ class TableBlockHolder(
|
|||
|
||||
fun bind(item: BlockView.Table) {
|
||||
selected.isSelected = item.isSelected
|
||||
lm.spanCount = item.rowCount
|
||||
tableAdapter.setTableBlockId(item.id)
|
||||
tableAdapter.submitList(item.cells)
|
||||
if (BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
(lm as CustomGridLayoutManager).spanCount = item.rowCount
|
||||
tableEditableCellsAdapter.setTableBlockId(item.id)
|
||||
tableEditableCellsAdapter.updateWithDiffUtil(item.cells)
|
||||
} else {
|
||||
(lm as GridLayoutManager).spanCount = item.rowCount
|
||||
tableAdapter.setTableBlockId(item.id)
|
||||
tableAdapter.submitList(item.cells)
|
||||
}
|
||||
}
|
||||
|
||||
fun processChangePayload(
|
||||
|
|
|
@ -4,8 +4,10 @@ import android.graphics.Canvas
|
|||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.CustomGridLayoutManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.presentation.BuildConfig
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class TableVerticalItemDivider(
|
||||
|
@ -18,7 +20,7 @@ class TableVerticalItemDivider(
|
|||
state: RecyclerView.State
|
||||
) {
|
||||
canvas.save()
|
||||
val spanCount = (parent.layoutManager as GridLayoutManager).spanCount
|
||||
val spanCount = getSpanCount(parent)
|
||||
val childCount = parent.childCount
|
||||
val itemCount = parent.adapter?.itemCount ?: 0
|
||||
val rect = Rect()
|
||||
|
@ -49,7 +51,7 @@ class TableVerticalItemDivider(
|
|||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
val spanCount = (parent.layoutManager as GridLayoutManager).spanCount
|
||||
val spanCount = getSpanCount(parent)
|
||||
val itemCount = parent.adapter?.itemCount ?: 0
|
||||
val position = parent.getChildLayoutPosition(view)
|
||||
outRect.bottom = drawable.intrinsicHeight
|
||||
|
@ -57,4 +59,12 @@ class TableVerticalItemDivider(
|
|||
outRect.top = drawable.intrinsicHeight
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSpanCount(parent: RecyclerView): Int {
|
||||
return if (BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
(parent.layoutManager as CustomGridLayoutManager).spanCount
|
||||
} else {
|
||||
(parent.layoutManager as GridLayoutManager).spanCount
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/bg_table_cell_board_all" android:state_selected="true" />
|
||||
</selector>
|
|
@ -0,0 +1,27 @@
|
|||
<?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"
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="@dimen/item_block_table_cell_width"
|
||||
android:minHeight="@dimen/item_block_table_cell_height">
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
|
||||
android:id="@+id/textContent"
|
||||
style="@style/BlockCellTextContentStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/item_block_multi_select_mode_selector"
|
||||
android:focusable="true"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="@string/default_text_placeholder" />
|
||||
|
||||
<View
|
||||
android:id="@+id/selection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/item_block_cell_selected" />
|
||||
|
||||
</FrameLayout>
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="match_parent"
|
||||
android:minHeight="42dp" />
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="@dimen/item_block_table_cell_width"
|
||||
android:minHeight="@dimen/item_block_table_cell_height" />
|
|
@ -160,12 +160,6 @@ class TableBlockTest {
|
|||
|
||||
onItemView(0, R.id.recyclerTable).checkHasChildViewCount(4)
|
||||
|
||||
onItemView(0, R.id.recyclerTable).checkHasChildViewWithText(
|
||||
0,
|
||||
"",
|
||||
R.id.textContent
|
||||
).checkIsDisplayed()
|
||||
|
||||
onItemView(0, R.id.recyclerTable).checkHasChildViewWithText(
|
||||
1,
|
||||
row1Block1.content.asText().text,
|
||||
|
@ -197,13 +191,6 @@ class TableBlockTest {
|
|||
|
||||
onItemView(0, R.id.recyclerTable).checkHasChildViewCount(4)
|
||||
|
||||
|
||||
onItemView(0, R.id.recyclerTable).checkHasChildViewWithText(
|
||||
0,
|
||||
"",
|
||||
R.id.textContent
|
||||
).checkIsDisplayed()
|
||||
|
||||
onItemView(0, R.id.recyclerTable).checkHasChildViewWithText(
|
||||
1,
|
||||
row1Block1.content.asText().text,
|
||||
|
|
|
@ -13,6 +13,7 @@ android {
|
|||
testInstrumentationRunner config["test_runner"]
|
||||
buildConfigField "boolean", "ENABLE_LINK_APPERANCE_MENU", "true"
|
||||
buildConfigField "boolean", "NESTED_DECORATION_ENABLED", "true"
|
||||
buildConfigField "boolean", "USE_SIMPLE_TABLES_IN_EDITOR_EDDITING", "true"
|
||||
}
|
||||
|
||||
testOptions.unitTests.includeAndroidResources = true
|
||||
|
|
|
@ -107,6 +107,7 @@ import com.anytypeio.anytype.presentation.editor.editor.ext.update
|
|||
import com.anytypeio.anytype.presentation.editor.editor.ext.updateCursorAndEditMode
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ext.updateSelection
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ext.applyBordersToSelectedCells
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ext.findTableCellView
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ext.removeBordersFromCells
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ext.updateTableOfContentsViews
|
||||
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
|
||||
|
@ -2448,19 +2449,27 @@ class EditorViewModel(
|
|||
fun onBlockToolbarBlockActionsClicked() {
|
||||
Timber.d("onBlockToolbarBlockActionsClicked, ")
|
||||
val target = orchestrator.stores.focus.current().id
|
||||
val view = views.find { it.id == target } ?: return
|
||||
when (view) {
|
||||
is BlockView.Title -> {
|
||||
sendToast(CANNOT_OPEN_ACTION_MENU_FOR_TITLE_ERROR)
|
||||
val view = views.find { it.id == target }
|
||||
if (view == null) {
|
||||
val cell = views.findTableCellView(target)
|
||||
if (cell != null && cell is BlockView.Table.Cell.Text) {
|
||||
onShowSimpleTableWidgetClicked(target)
|
||||
viewModelScope.sendAnalyticsSelectionMenuEvent(analytics)
|
||||
}
|
||||
is BlockView.Description -> {
|
||||
sendToast(CANNOT_OPEN_ACTION_MENU_FOR_DESCRIPTION)
|
||||
}
|
||||
else -> {
|
||||
proceedWithEnteringActionMode(target = target, scrollTarget = false)
|
||||
} else {
|
||||
when (view) {
|
||||
is BlockView.Title -> {
|
||||
sendToast(CANNOT_OPEN_ACTION_MENU_FOR_TITLE_ERROR)
|
||||
}
|
||||
is BlockView.Description -> {
|
||||
sendToast(CANNOT_OPEN_ACTION_MENU_FOR_DESCRIPTION)
|
||||
}
|
||||
else -> {
|
||||
proceedWithEnteringActionMode(target = target, scrollTarget = false)
|
||||
}
|
||||
}
|
||||
viewModelScope.sendAnalyticsSelectionMenuEvent(analytics)
|
||||
}
|
||||
viewModelScope.sendAnalyticsSelectionMenuEvent(analytics)
|
||||
}
|
||||
|
||||
fun onEnterScrollAndMoveClicked() {
|
||||
|
@ -2775,6 +2784,10 @@ class EditorViewModel(
|
|||
|
||||
private fun fillTableBlockRow(cellId: Id, targetIds: List<Id>, tableId: Id) {
|
||||
viewModelScope.launch {
|
||||
if (BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
val focus = Editor.Focus(id = cellId, cursor = null)
|
||||
orchestrator.stores.focus.update(focus)
|
||||
}
|
||||
orchestrator.proxies.intents.send(
|
||||
Intent.Table.FillTableRow(
|
||||
ctx = context,
|
||||
|
@ -2782,13 +2795,15 @@ class EditorViewModel(
|
|||
)
|
||||
)
|
||||
}
|
||||
dispatch(
|
||||
Command.OpenSetBlockTextValueScreen(
|
||||
ctx = context,
|
||||
block = cellId,
|
||||
table = tableId
|
||||
if (!BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
dispatch(
|
||||
Command.OpenSetBlockTextValueScreen(
|
||||
ctx = context,
|
||||
block = cellId,
|
||||
table = tableId
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddDividerBlockClicked(style: Content.Divider.Style) {
|
||||
|
@ -3827,10 +3842,31 @@ class EditorViewModel(
|
|||
}
|
||||
is ListenerType.TableEmptyCell -> {
|
||||
when (mode) {
|
||||
EditorMode.Edit, EditorMode.Locked -> {
|
||||
if (currentSelection().isNotEmpty()) {
|
||||
Timber.e("Some other blocks are selected, amend table cell click")
|
||||
return
|
||||
EditorMode.Locked -> {
|
||||
if (BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
Timber.d("Clicked on table cell in Locked Mode")
|
||||
} else {
|
||||
if (currentSelection().isNotEmpty()) {
|
||||
Timber.e("Some other blocks are selected, amend table cell click")
|
||||
return
|
||||
}
|
||||
proceedWithSelectingCell(
|
||||
cellId = clicked.cellId,
|
||||
tableId = clicked.tableId
|
||||
)
|
||||
onTableRowEmptyCellClicked(
|
||||
cellId = clicked.cellId,
|
||||
rowId = clicked.rowId,
|
||||
tableId = clicked.tableId
|
||||
)
|
||||
}
|
||||
}
|
||||
EditorMode.Edit -> {
|
||||
if (!BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
if (currentSelection().isNotEmpty()) {
|
||||
Timber.e("Some other blocks are selected, amend table cell click")
|
||||
return
|
||||
}
|
||||
}
|
||||
proceedWithSelectingCell(
|
||||
cellId = clicked.cellId,
|
||||
|
@ -3848,7 +3884,7 @@ class EditorViewModel(
|
|||
}
|
||||
is ListenerType.TableTextCell -> {
|
||||
when (mode) {
|
||||
EditorMode.Edit, EditorMode.Locked -> {
|
||||
EditorMode.Edit -> {
|
||||
if (currentSelection().isNotEmpty()) {
|
||||
Timber.e("Some other blocks are selected, amend table cell click")
|
||||
return
|
||||
|
@ -3857,13 +3893,34 @@ class EditorViewModel(
|
|||
cellId = clicked.cellId,
|
||||
tableId = clicked.tableId
|
||||
)
|
||||
dispatch(
|
||||
Command.OpenSetBlockTextValueScreen(
|
||||
ctx = context,
|
||||
block = clicked.cellId,
|
||||
table = clicked.tableId
|
||||
if (!BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
dispatch(
|
||||
Command.OpenSetBlockTextValueScreen(
|
||||
ctx = context,
|
||||
block = clicked.cellId,
|
||||
table = clicked.tableId
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
EditorMode.Locked -> {
|
||||
if (currentSelection().isNotEmpty()) {
|
||||
Timber.e("Some other blocks are selected, amend table cell click")
|
||||
return
|
||||
}
|
||||
if (!BuildConfig.USE_SIMPLE_TABLES_IN_EDITOR_EDDITING) {
|
||||
proceedWithSelectingCell(
|
||||
cellId = clicked.cellId,
|
||||
tableId = clicked.tableId
|
||||
)
|
||||
dispatch(
|
||||
Command.OpenSetBlockTextValueScreen(
|
||||
ctx = context,
|
||||
block = clicked.cellId,
|
||||
table = clicked.tableId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
EditorMode.Select -> onBlockMultiSelectClicked(target = clicked.tableId)
|
||||
else -> Unit
|
||||
|
|
|
@ -1149,4 +1149,18 @@ fun List<BlockView>.removeBordersFromCells(): List<BlockView> = map { view ->
|
|||
} else {
|
||||
view
|
||||
}
|
||||
}
|
||||
|
||||
fun List<BlockView>.findTableCellView(id: Id): BlockView.Table.Cell? {
|
||||
forEach { blockView ->
|
||||
if (blockView is BlockView.Table) {
|
||||
val cells = blockView.cells
|
||||
val cell = cells.find {
|
||||
it is BlockView.Table.Cell.Empty && it.getId() == id
|
||||
|| it is BlockView.Table.Cell.Text && it.getId() == id
|
||||
}
|
||||
if (cell != null) return cell
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
|
@ -19,8 +19,7 @@ import org.junit.Before
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.inOrder
|
||||
import org.mockito.kotlin.verifyNoInteractions
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
@ -38,7 +37,7 @@ class EditorTableBlockTest : EditorPresentationTestSetup() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `should amend second empty cell click`() {
|
||||
fun `should not amend second empty cell click`() {
|
||||
|
||||
val columns = StubTableColumns(size = 3)
|
||||
val rows = StubTableRows(size = 2)
|
||||
|
@ -85,16 +84,16 @@ class EditorTableBlockTest : EditorPresentationTestSetup() {
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
val selectedState = vm.currentSelection()
|
||||
|
||||
assertEquals(1, selectedState.size)
|
||||
assertEquals(cell1Id, selectedState.first())
|
||||
coroutineTestRule.advanceTime(50L)
|
||||
|
||||
runBlocking {
|
||||
verify(fillTableRow, times(1)).invoke(
|
||||
val inOrder = inOrder(fillTableRow)
|
||||
inOrder.verify(fillTableRow).invoke(
|
||||
params = FillTableRow.Params(ctx = root, targetIds = listOf(rows[0].id))
|
||||
)
|
||||
inOrder.verify(fillTableRow).invoke(
|
||||
params = FillTableRow.Params(ctx = root, targetIds = listOf(rows[1].id))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue