1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

Editor | Enhancement | Calculate decoration data for callout block and its children (#2383)

This commit is contained in:
Evgenii Kozlov 2022-06-23 16:23:24 +03:00 committed by GitHub
parent 5fe33a8d72
commit 851e5c82bf
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 339 additions and 17 deletions

View file

@ -152,6 +152,12 @@ sealed class BlockView : ViewType {
object Middle: Highlight()
object End : Highlight()
}
sealed class Callout : Style() {
object Start : Callout()
object Middle: Callout()
object End : Callout()
object Full : Callout()
}
sealed class Header: Style() {
object H1 : Header()
object H2 : Header()

View file

@ -5,7 +5,6 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.model.Indent
/**
* Mapping indent level to gathered information about parents (ancestors) styles during document graph traversal.
@ -28,17 +27,18 @@ data class DecorationData(
* [end] value is calculated while graph traversal and depends on the depth / indent level.
*/
data class Highlight(val start: Id, val end: Id) : Style()
/**
* @property [start] id of the starting block for callout style
* @property [end] id of the last block for callout style.
* [end] value is calculated while graph traversal and depends on the depth / indent level.
*/
data class Callout(val start: Id, val end: Id) : Style()
sealed class Header : Style() {
object H1 : Header()
object H2 : Header()
object H3 : Header()
}
object Card : Style()
/**
* Add style for [Block.Content.Text.Style.CALLOUT] when it is supported by [DefaultBlockViewRenderer]
*/
//
//data class Callout(val start: Id, val end: Id) : Style()
}
}
@ -67,6 +67,20 @@ fun buildNestedDecorationData(
add(holder)
}
}
is DecorationData.Style.Callout -> {
if (block.id == style.end) {
// Block is a closing block for a callout style
if (block.children.isEmpty()) {
add(holder)
} else {
// But it has children, so its last child is supposed to be a closing block.
add(holder.copy(style = style.copy(end = block.children.last())))
}
} else {
// It is not a closing block for callout, no need to normalize it.
add(holder)
}
}
else -> add(holder)
}
}
@ -95,6 +109,29 @@ fun NestedDecorationData.toBlockViewDecoration(block: Block): List<BlockView.Dec
)
}
}
is DecorationData.Style.Callout -> {
if (style.start == style.end) {
BlockView.Decoration(
style = BlockView.Decoration.Style.Callout.Full,
background = holder.background
)
} else if (style.start == block.id) {
BlockView.Decoration(
style = BlockView.Decoration.Style.Callout.Start,
background = holder.background
)
} else if (style.end == block.id) {
BlockView.Decoration(
style = BlockView.Decoration.Style.Callout.End,
background = holder.background
)
} else {
BlockView.Decoration(
style = BlockView.Decoration.Style.Callout.Middle,
background = holder.background
)
}
}
is DecorationData.Style.Header.H1 -> {
BlockView.Decoration(
style = BlockView.Decoration.Style.Header.H1,
@ -148,6 +185,19 @@ fun normalizeNestedDecorationData(
holder
}
}
is DecorationData.Style.Callout -> {
if (block.id == style.end) {
if (block.children.isEmpty()) {
holder
} else {
holder.copy(
style = style.copy(end = block.children.last())
)
}
} else {
holder
}
}
else -> holder
}
}

View file

@ -518,8 +518,17 @@ class DefaultBlockViewRenderer @Inject constructor(
}
Content.Text.Style.CALLOUT -> {
mCounter = 0
// TODO support nesting background
val normalized = emptyList<DecorationData>()
val blockDecorationScheme: NestedDecorationData = buildNestedDecorationData(
block = block,
parentScheme = parentScheme,
currentDecoration = DecorationData(
style = DecorationData.Style.Callout(
start = block.id,
end = block.children.lastOrNull() ?: block.id
),
background = block.parseThemeBackgroundColor()
)
)
result.add(
callout(
mode = mode,
@ -529,7 +538,7 @@ class DefaultBlockViewRenderer @Inject constructor(
indent = indent,
details = details,
selection = selection,
scheme = normalized
scheme = blockDecorationScheme
)
)
if (block.children.isNotEmpty()) {
@ -546,8 +555,7 @@ class DefaultBlockViewRenderer @Inject constructor(
selection = selection,
objectTypes = objectTypes,
onRenderFlag = onRenderFlag,
// TODO support nesting background
parentScheme = emptyList()
parentScheme = blockDecorationScheme
)
)
}
@ -1033,8 +1041,6 @@ class DefaultBlockViewRenderer @Inject constructor(
details = details,
marks = marks
)
// TODO support nesting
val current = emptyList<BlockView.Decoration>()
val iconImage = content.iconImage
val iconEmoji = content.iconEmoji
val icon = when {
@ -1057,7 +1063,7 @@ class DefaultBlockViewRenderer @Inject constructor(
block = block,
selection = selection
),
decorations = scheme.toBlockViewDecoration(block) + current,
decorations = scheme.toBlockViewDecoration(block),
icon = icon
)
}

View file

@ -8,6 +8,7 @@ import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.SmartBlockType
import com.anytypeio.anytype.core_models.StubBookmark
import com.anytypeio.anytype.core_models.StubCallout
import com.anytypeio.anytype.core_models.StubParagraph
import com.anytypeio.anytype.core_models.StubSmartBlock
import com.anytypeio.anytype.core_models.ext.asMap
@ -29,7 +30,6 @@ import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.render.BlockViewRenderer
import com.anytypeio.anytype.presentation.editor.render.DecorationData
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.render.NestedDecorationData
import com.anytypeio.anytype.presentation.editor.render.parseThemeBackgroundColor
@ -4451,6 +4451,10 @@ class DefaultBlockViewRendererTest {
// Quote nesting
/**
* Q with background
* ...P with background
*/
@Test
fun `should return blocks with expected decoration - when a quote contains a paragraph`() {
val child = Block(
@ -5388,4 +5392,258 @@ class DefaultBlockViewRendererTest {
assertEquals(expected = expected, actual = result)
}
//region Callout nesting
/**
* C with background. No children.
*/
@Test
fun `should return blocks with expected decoration - when a callout without children has background`() {
val callout = StubCallout(backgroundColor = ThemeColor.BLUE.code)
val page = StubSmartBlock(children = listOf(callout.id))
val details = mapOf(page.id to Block.Fields.empty())
val blocks = listOf(page, callout)
val map = blocks.asMap()
wrapper = BlockViewRenderWrapper(
blocks = map,
renderer = renderer
)
val result = runBlocking {
wrapper.render(
root = page,
anchor = page.id,
focus = Editor.Focus.empty(),
indent = 0,
details = Block.Details(details)
)
}
val expected = listOf(
BlockView.Text.Callout(
indent = 0,
isFocused = false,
id = callout.id,
marks = emptyList(),
backgroundColor = callout.backgroundColor,
color = callout.content<Block.Content.Text>().color,
text = callout.content<Block.Content.Text>().text,
decorations = if (BuildConfig.NESTED_DECORATION_ENABLED) {
listOf(
BlockView.Decoration(
style = BlockView.Decoration.Style.Callout.Full,
background = callout.parseThemeBackgroundColor()
)
)
} else {
emptyList()
},
icon = ObjectIcon.Basic.Emoji("💡")
)
)
assertEquals(expected = expected, actual = result)
}
/**
* C with background
* ...P with background
*/
@Test
fun `should return blocks with expected decoration - when a callout contains a paragraph`() {
val child = StubParagraph(backgroundColor = ThemeColor.ORANGE.code)
val callout = StubCallout(
children = listOf(child.id),
backgroundColor = ThemeColor.BLUE.code
)
val page = StubSmartBlock(children = listOf(callout.id))
val details = mapOf(page.id to Block.Fields.empty())
val blocks = listOf(page, callout, child)
val map = blocks.asMap()
wrapper = BlockViewRenderWrapper(
blocks = map,
renderer = renderer
)
val result = runBlocking {
wrapper.render(
root = page,
anchor = page.id,
focus = Editor.Focus.empty(),
indent = 0,
details = Block.Details(details)
)
}
val expected = listOf(
BlockView.Text.Callout(
indent = 0,
isFocused = false,
id = callout.id,
marks = emptyList(),
backgroundColor = callout.backgroundColor,
color = callout.content<Block.Content.Text>().color,
text = callout.content<Block.Content.Text>().text,
decorations = if (BuildConfig.NESTED_DECORATION_ENABLED) {
listOf(
BlockView.Decoration(
style = BlockView.Decoration.Style.Callout.Start,
background = callout.parseThemeBackgroundColor()
)
)
} else {
emptyList()
},
icon = ObjectIcon.Basic.Emoji("💡")
),
BlockView.Text.Paragraph(
indent = 1,
isFocused = false,
id = child.id,
marks = emptyList(),
backgroundColor = child.backgroundColor,
color = child.content<Block.Content.Text>().color,
text = child.content<Block.Content.Text>().text,
decorations = if (BuildConfig.NESTED_DECORATION_ENABLED) {
listOf(
BlockView.Decoration(
background = callout.parseThemeBackgroundColor(),
style = BlockView.Decoration.Style.Callout.End
),
BlockView.Decoration(
background = child.parseThemeBackgroundColor()
)
)
} else {
emptyList()
}
)
)
assertEquals(expected = expected, actual = result)
}
/**
* C with background
* ...P with background
* ...P with background
*/
@Test
fun `should return blocks with expected decoration - when a callout with background contains two paragraphs, each paragraph has its own background`() {
val child1 = StubParagraph(backgroundColor = ThemeColor.ORANGE.code)
val child2 = StubParagraph(backgroundColor = ThemeColor.ORANGE.code)
val callout = StubCallout(
children = listOf(child1.id, child2.id),
backgroundColor = ThemeColor.BLUE.code
)
val page = StubSmartBlock(children = listOf(callout.id))
val details = mapOf(page.id to Block.Fields.empty())
val blocks = listOf(page, callout, child1, child2)
val map = blocks.asMap()
wrapper = BlockViewRenderWrapper(
blocks = map,
renderer = renderer
)
val result = runBlocking {
wrapper.render(
root = page,
anchor = page.id,
focus = Editor.Focus.empty(),
indent = 0,
details = Block.Details(details)
)
}
val expected = listOf(
BlockView.Text.Callout(
indent = 0,
isFocused = false,
id = callout.id,
marks = emptyList(),
backgroundColor = callout.backgroundColor,
color = callout.content<Block.Content.Text>().color,
text = callout.content<Block.Content.Text>().text,
decorations = if (BuildConfig.NESTED_DECORATION_ENABLED) {
listOf(
BlockView.Decoration(
style = BlockView.Decoration.Style.Callout.Start,
background = callout.parseThemeBackgroundColor()
)
)
} else {
emptyList()
},
icon = ObjectIcon.Basic.Emoji("💡")
),
BlockView.Text.Paragraph(
indent = 1,
isFocused = false,
id = child1.id,
marks = emptyList(),
backgroundColor = child1.backgroundColor,
color = child1.content<Block.Content.Text>().color,
text = child1.content<Block.Content.Text>().text,
decorations = if (BuildConfig.NESTED_DECORATION_ENABLED) {
listOf(
BlockView.Decoration(
background = callout.parseThemeBackgroundColor(),
style = BlockView.Decoration.Style.Callout.Middle
),
BlockView.Decoration(
background = child1.parseThemeBackgroundColor()
)
)
} else {
emptyList()
}
),
BlockView.Text.Paragraph(
indent = 1,
isFocused = false,
id = child2.id,
marks = emptyList(),
backgroundColor = child2.backgroundColor,
color = child2.content<Block.Content.Text>().color,
text = child2.content<Block.Content.Text>().text,
decorations = if (BuildConfig.NESTED_DECORATION_ENABLED) {
listOf(
BlockView.Decoration(
background = callout.parseThemeBackgroundColor(),
style = BlockView.Decoration.Style.Callout.End
),
BlockView.Decoration(
background = child2.parseThemeBackgroundColor()
)
)
} else {
emptyList()
}
)
)
assertEquals(expected = expected, actual = result)
}
//endregion
}

View file

@ -148,7 +148,8 @@ fun StubQuote(
fun StubCallout(
text: String = MockDataFactory.randomString(),
children: List<Id> = emptyList(),
marks: List<Block.Content.Text.Mark> = emptyList()
marks: List<Block.Content.Text.Mark> = emptyList(),
backgroundColor: String? = null
): Block = Block(
id = MockDataFactory.randomUuid(),
content = StubTextContent(
@ -157,7 +158,8 @@ fun StubCallout(
marks = marks
),
children = children,
fields = Block.Fields.empty()
fields = Block.Fields.empty(),
backgroundColor = backgroundColor
)
fun StubRelation(