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

Editor | Fix | Style toolbars refactoring (#2238)

* legacy

* added model for style toolbars state

* ext fun for getting text style toolbar state + tests

* move text style toolbar to use new state

* moving the style background toolbar to a new state

* update control panel events

* update control machine event for background toolbar

* code block uses cursor

* close kayboard on opening background toolbar

* refresh background toolbar

* refresh bakground toolbar 2

* send blocks background update command

* start background toolbar for code block and style toolbar for others

* update style color toolbar

* update style color toolbar with state

* update style color toolbar vm logic

* get style color toolbar state

* style other toolbar new state

* get style other toolbar state + vm logic

* legacy styleconfig model

* legacy

* on style event click refactoring

* on close background toolbar event

* added new command, blockList set mark

* remove doubles

* markup type to core model

* legacy

* update markup for several text blocks

* update style other toolbar with selected states

* toolbars viewmodels logic

* legacy, code style

* code style

* fixed tests

* test fix

Co-authored-by: konstantiniiv <ki@anytype.io>
This commit is contained in:
Konstantin Ivanov 2022-05-10 15:11:13 +03:00 committed by GitHub
parent a2d2189376
commit 2e3e22aacd
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 804 additions and 2770 deletions

View file

@ -1378,7 +1378,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
state.styleTextToolbar.apply {
val behavior = BottomSheetBehavior.from(binding.styleToolbarMain)
if (isVisible) {
binding.styleToolbarMain.setSelectedStyle(style)
binding.styleToolbarMain.setSelectedStyle(this.state)
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
keyboardDelayJobs += lifecycleScope.launch {
if (binding.recycler.itemDecorationCount == 0) {
@ -1411,10 +1411,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
state.styleExtraToolbar.apply {
if (isVisible) {
binding.styleToolbarOther.setProperties(
props = state.styleTextToolbar.props,
config = state.styleTextToolbar.config
)
binding.styleToolbarOther.setProperties(state.styleExtraToolbar.state)
BottomSheetBehavior.from(binding.styleToolbarOther).apply {
setState(BottomSheetBehavior.STATE_EXPANDED)
addBottomSheetCallback(onHideBottomSheetCallback)
@ -1429,12 +1426,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
state.styleColorBackgroundToolbar.apply {
if (isVisible) {
state.styleTextToolbar.config?.let { config ->
binding.styleToolbarColors.update(
config,
state.styleTextToolbar.props
)
}
binding.styleToolbarColors.update(state.styleColorBackgroundToolbar.state)
BottomSheetBehavior.from(binding.styleToolbarColors).apply {
setState(BottomSheetBehavior.STATE_EXPANDED)
addBottomSheetCallback(onHideBottomSheetCallback)
@ -1448,13 +1440,29 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
}
state.styleBackgroundToolbar.apply {
val behavior = BottomSheetBehavior.from(binding.styleToolbarBackground)
if (isVisible) {
state.styleBackgroundToolbar.selectedBackground.let {
state.styleBackgroundToolbar.state.let {
binding.styleToolbarBackground.update(it)
}
BottomSheetBehavior.from(binding.styleToolbarBackground).apply {
setState(BottomSheetBehavior.STATE_EXPANDED)
addBottomSheetCallback(onHideBottomSheetCallback)
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
keyboardDelayJobs += lifecycleScope.launch {
if (binding.recycler.itemDecorationCount == 0) {
binding.recycler.addItemDecoration(styleToolbarFooter)
}
proceedWithHidingSoftInput()
if (insets != null) {
if (insets.isVisible(WindowInsetsCompat.Type.ime())) {
delay(DELAY_HIDE_KEYBOARD)
}
} else {
delay(DELAY_HIDE_KEYBOARD)
}
behavior.apply {
setState(BottomSheetBehavior.STATE_EXPANDED)
addBottomSheetCallback(onHideBottomSheetCallback)
}
}
}
} else {
BottomSheetBehavior.from(binding.styleToolbarBackground).apply {

View file

@ -276,4 +276,9 @@ fun List<Block>.getChildrenIdsList(parent: Id): List<String> {
fun List<Block>.isAllTextAndNoneCodeBlocks(): Boolean =
all { block ->
block.content is Content.Text && block.content.style != Content.Text.Style.CODE_SNIPPET
}
fun List<Block>.isAllTextBlocks(): Boolean =
all { block ->
block.content is Content.Text
}

View file

@ -71,6 +71,7 @@ class Code(val binding: ItemBlockCodeSnippetBinding) : BlockViewHolder(binding.r
setBackgroundColor(item.backgroundColor)
setCursor(item)
setFocus(item)
content.addTextChangedListener(
@ -158,6 +159,10 @@ class Code(val binding: ItemBlockCodeSnippetBinding) : BlockViewHolder(binding.r
setFocus(item)
}
if (payload.isCursorChanged) {
setCursor(item = item)
}
if (payload.backgroundColorChanged()) {
setBackgroundColor(item.backgroundColor)
}
@ -196,6 +201,18 @@ class Code(val binding: ItemBlockCodeSnippetBinding) : BlockViewHolder(binding.r
}
}
private fun setCursor(item: BlockView.Code) {
if (item.isFocused) {
Timber.d("Setting cursor: $item")
item.cursor?.let {
val length = content.text?.length ?: 0
if (it in 0..length) {
content.setSelection(it)
}
}
}
}
private fun setBackgroundColor(color: String? = null) {
Timber.d("Setting background color: $color")
val value = ThemeColor.values().find { value -> value.title == color }

View file

@ -12,6 +12,7 @@ import com.anytypeio.anytype.core_ui.databinding.WidgetStyleToolbarMainBinding
import com.anytypeio.anytype.core_ui.extensions.toast
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.presentation.editor.editor.model.UiBlock
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@ -48,8 +49,8 @@ class StyleToolbarMainWidget @JvmOverloads constructor(
val other = binding.dots.clicks()
val colors = binding.markupColors.clicks()
fun setSelectedStyle(style: TextStyle?) {
when (style) {
fun setSelectedStyle(state: StyleToolbarState.Text?) {
when (state?.textStyle) {
TextStyle.P -> select(binding.tvStyleText.id)
TextStyle.H1 -> select(binding.tvStyleTitle.id)
TextStyle.H2 -> select(binding.tvStyleHeading.id)

View file

@ -6,10 +6,7 @@ import android.view.LayoutInflater
import androidx.cardview.widget.CardView
import com.anytypeio.anytype.core_ui.databinding.WidgetBlockStyleExtraBinding
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@ -36,36 +33,31 @@ class StyleToolbarExtraWidget @JvmOverloads constructor(
)
fun setProperties(
props: ControlPanelState.Toolbar.Styling.Props?,
config: StyleConfig?
state: StyleToolbarState.Other
) = with(binding) {
bold.isSelected = props?.isBold ?: false
italic.isSelected = props?.isItalic ?: false
strikethrough.isSelected = props?.isStrikethrough ?: false
code.isSelected = props?.isCode ?: false
alignmentLeft.isSelected = props?.alignment == Alignment.START
alignmentMiddle.isSelected = props?.alignment == Alignment.CENTER
alignmentRight.isSelected = props?.alignment == Alignment.END
setUrl.isSelected = props?.isLinked ?: false
bold.isSelected = state.isBoldSelected
italic.isSelected = state.isItalicSelected
strikethrough.isSelected = state.isStrikethroughSelected
code.isSelected = state.isCodeSelected
alignmentLeft.isSelected = state.isAlignStartSelected
alignmentMiddle.isSelected = state.isAlignCenterSelected
alignmentRight.isSelected = state.isAlignEndSelected
config?.let {
alignmentLeft.isEnabled = it.enabledAlignment.contains(Alignment.START)
alignmentLeftIcon.isEnabled = alignmentLeft.isEnabled
alignmentRight.isEnabled = it.enabledAlignment.contains(Alignment.END)
alignmentRightIcon.isEnabled = alignmentRight.isEnabled
alignmentMiddle.isEnabled = it.enabledAlignment.contains(Alignment.CENTER)
alignmentMiddleIcon.isEnabled = alignmentMiddle.isEnabled
bold.isEnabled = it.enabledMarkup.contains(Markup.Type.BOLD)
boldIcon.isEnabled = bold.isEnabled
italic.isEnabled = it.enabledMarkup.contains(Markup.Type.ITALIC)
italicIcon.isEnabled = italic.isEnabled
strikethrough.isEnabled = it.enabledMarkup.contains(Markup.Type.STRIKETHROUGH)
strikethroughIcon.isEnabled = strikethrough.isEnabled
code.isEnabled = it.enabledMarkup.contains(Markup.Type.KEYBOARD)
codeIcon.isEnabled = code.isEnabled
setUrl.isEnabled = it.enabledMarkup.contains(Markup.Type.LINK)
setUrlIcon.isEnabled = setUrl.isEnabled
}
alignmentLeft.isEnabled = state.isSupportAlignStart
alignmentLeftIcon.isEnabled = alignmentLeft.isEnabled
alignmentRight.isEnabled = state.isSupportAlignEnd
alignmentRightIcon.isEnabled = alignmentRight.isEnabled
alignmentMiddle.isEnabled = state.isSupportAlignCenter
alignmentMiddleIcon.isEnabled = alignmentMiddle.isEnabled
bold.isEnabled = state.isSupportBold
boldIcon.isEnabled = bold.isEnabled
italic.isEnabled = state.isSupportItalic
italicIcon.isEnabled = italic.isEnabled
strikethrough.isEnabled = state.isSupportStrikethrough
strikethroughIcon.isEnabled = strikethrough.isEnabled
code.isEnabled = state.isSupportCode
codeIcon.isEnabled = code.isEnabled
setUrl.isEnabled = state.isSupportLinked
setUrlIcon.isEnabled = setUrl.isEnabled
}
}

View file

@ -5,30 +5,17 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.core_ui.databinding.BlockStyleToolbarBackgroundBinding
import com.anytypeio.anytype.core_ui.databinding.BlockStyleToolbarColorBinding
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingType
class StyleAdapter(
var props: ControlPanelState.Toolbar.Styling.Props?,
private val visibleTypes: ArrayList<StylingType>,
private val enabledMarkup: ArrayList<Markup.Type>,
private val enabledAlignment: ArrayList<Alignment>,
private var state: StyleToolbarState.ColorBackground,
private val onStylingEvent: (StylingEvent) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
@Deprecated("Maybe legacy, maybe not.")
fun updateConfig(config: StyleConfig, props: ControlPanelState.Toolbar.Styling.Props?) {
visibleTypes.clear()
visibleTypes.addAll(config.visibleTypes)
enabledMarkup.clear()
enabledMarkup.addAll(config.enabledMarkup)
enabledAlignment.clear()
enabledAlignment.addAll(config.enabledAlignment)
this.props = props
fun update(state: StyleToolbarState.ColorBackground) {
this.state = state
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
@ -51,10 +38,10 @@ class StyleAdapter(
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is StyleTextColorViewHolder -> {
holder.bind(onStylingEvent, props?.color)
holder.bind(onStylingEvent, state.color)
}
is StyleBackgroundViewHolder -> {
holder.bind(onStylingEvent, props?.background)
holder.bind(onStylingEvent, state.background)
}
}
}

View file

@ -7,6 +7,7 @@ import androidx.cardview.widget.CardView
import com.anytypeio.anytype.core_ui.databinding.WidgetBlockStyleToolbarBackgroundBinding
import com.anytypeio.anytype.core_ui.reactive.clicks
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@ -46,19 +47,19 @@ class StyleBackgroundToolbarWidget @JvmOverloads constructor(
.map { StylingEvent.Coloring.Background(ThemeColor.GREEN) }
)
fun update(background: String?) {
fun update(state: StyleToolbarState.Background) {
with(binding.flowColors) {
backgroundColorDefault.isSelected = background == ThemeColor.DEFAULT.title
backgroundColorGrey.isSelected = background == ThemeColor.GREY.title
backgroundColorYellow.isSelected = background == ThemeColor.YELLOW.title
backgroundColorOrange.isSelected = background == ThemeColor.ORANGE.title
backgroundColorRed.isSelected = background == ThemeColor.RED.title
backgroundColorPink.isSelected = background == ThemeColor.PINK.title
backgroundColorPurple.isSelected = background == ThemeColor.PURPLE.title
backgroundColorBlue.isSelected = background == ThemeColor.BLUE.title
backgroundColorIce.isSelected = background == ThemeColor.ICE.title
backgroundColorTeal.isSelected = background == ThemeColor.TEAL.title
backgroundColorGreen.isSelected = background == ThemeColor.GREEN.title
backgroundColorDefault.isSelected = state.background == ThemeColor.DEFAULT.title
backgroundColorGrey.isSelected = state.background == ThemeColor.GREY.title
backgroundColorYellow.isSelected = state.background == ThemeColor.YELLOW.title
backgroundColorOrange.isSelected = state.background == ThemeColor.ORANGE.title
backgroundColorRed.isSelected = state.background == ThemeColor.RED.title
backgroundColorPink.isSelected = state.background == ThemeColor.PINK.title
backgroundColorPurple.isSelected = state.background == ThemeColor.PURPLE.title
backgroundColorBlue.isSelected = state.background == ThemeColor.BLUE.title
backgroundColorIce.isSelected = state.background == ThemeColor.ICE.title
backgroundColorTeal.isSelected = state.background == ThemeColor.TEAL.title
backgroundColorGreen.isSelected = state.background == ThemeColor.GREEN.title
}
}
}

View file

@ -7,8 +7,7 @@ import android.widget.TextView
import androidx.cardview.widget.CardView
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.WidgetBlockStyleToolbarColorsBinding
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.channels.Channel
@ -23,10 +22,7 @@ class StyleColorToolbarWidget @JvmOverloads constructor(
private val channel = Channel<StylingEvent>()
private val blockStyleAdapter = StyleAdapter(
props = null,
visibleTypes = arrayListOf(),
enabledAlignment = arrayListOf(),
enabledMarkup = arrayListOf()
state = StyleToolbarState.ColorBackground.empty()
) { event ->
Timber.d("Styling Event : $event")
channel.trySend(event)
@ -56,8 +52,7 @@ class StyleColorToolbarWidget @JvmOverloads constructor(
}.attach()
}
fun update(config: StyleConfig, props: ControlPanelState.Toolbar.Styling.Props?) {
blockStyleAdapter.updateConfig(config, props)
blockStyleAdapter.notifyDataSetChanged()
fun update(state: StyleToolbarState.ColorBackground) {
blockStyleAdapter.update(state)
}
}

View file

@ -2,24 +2,16 @@ package com.anytypeio.anytype.presentation.editor
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.TextBlock
import com.anytypeio.anytype.core_models.TextStyle
import com.anytypeio.anytype.core_models.ext.overlap
import com.anytypeio.anytype.core_models.misc.Overlap
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.*
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState.Companion.init
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState.Toolbar
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashWidgetState
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.getSupportedMarkupTypes
import com.anytypeio.anytype.presentation.extension.*
import com.anytypeio.anytype.presentation.mapper.marks
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
import kotlinx.coroutines.CoroutineScope
@ -96,12 +88,6 @@ sealed class ControlPanelMachine {
object OnBlockStyleSelected : Event()
/**
* Represents an event when user selected block background color on [Toolbar.Styling] toolbar.
*/
object OnBlockBackgroundColorSelected : Event()
/**
* Represents an event when user cleares the current focus by closing keyboard.
*/
@ -122,23 +108,6 @@ sealed class ControlPanelMachine {
val style: Block.Content.Text.Style
) : Event()
data class OnBlockActionToolbarStyleClicked(
val target: Block,
val focused: Boolean,
val selection: IntRange?,
val urlBuilder: UrlBuilder,
val details: Block.Details
) : Event()
data class OnMultiSelectTextStyleClicked(
val target: Toolbar.Styling.Target?,
val config: StyleConfig?,
val props: Toolbar.Styling.Props?,
val style: TextStyle?
) : Event()
data class OnMultiSelectBackgroundStyleClicked(val selectedBackground: String?) : Event()
sealed class SearchToolbar : Event() {
object OnEnterSearchMode : SearchToolbar()
object OnExitSearchMode : SearchToolbar()
@ -155,8 +124,14 @@ sealed class ControlPanelMachine {
*/
sealed class StylingToolbar : Event() {
object OnExtraClicked : StylingToolbar()
object OnColorBackgroundClicked : StylingToolbar()
data class OnUpdateTextToolbar(val state: StyleToolbarState.Text) : StylingToolbar()
data class OnUpdateBackgroundToolbar(val state: StyleToolbarState.Background) :
StylingToolbar()
data class OnUpdateColorBackgroundToolbar(val state: StyleToolbarState.ColorBackground) :
StylingToolbar()
data class OnUpdateOtherToolbar(val state: StyleToolbarState.Other) : StylingToolbar()
object OnExtraClosed : StylingToolbar()
object OnColorBackgroundClosed : StylingToolbar()
data class OnClose(val focused: Boolean) : StylingToolbar()
@ -222,19 +197,7 @@ sealed class ControlPanelMachine {
}
sealed class OnRefresh : Event() {
data class StyleToolbar(
val target: Block?,
val selection: IntRange?,
val urlBuilder: UrlBuilder,
val details: Block.Details
) : OnRefresh()
data class Markup(val target: Block?, val selection: IntRange?) : OnRefresh()
data class StyleToolbarMulti(
val target: Toolbar.Styling.Target?,
val config: StyleConfig?,
val props: Toolbar.Styling.Props?,
val style: TextStyle?
) : OnRefresh()
}
object OnDocumentMenuClicked : Event()
@ -394,7 +357,6 @@ sealed class ControlPanelMachine {
}
is Event.OnMarkupTextColorSelected -> state.copy()
is Event.OnBlockTextColorSelected -> state.copy()
is Event.OnBlockBackgroundColorSelected -> state.copy()
is Event.OnBlockStyleSelected -> state.copy()
is Event.OnAddBlockToolbarOptionSelected -> state.copy()
is Event.OnMarkupBackgroundColorSelected -> state.copy()
@ -417,78 +379,6 @@ sealed class ControlPanelMachine {
)
}
}
is Event.OnBlockActionToolbarStyleClicked -> {
val target = target(
block = event.target,
details = event.details,
urlBuilder = event.urlBuilder
)
val style = event.target.let {
val content = it.content
check(content is Block.Content.Text)
content.style
}
state.copy(
mainToolbar = state.mainToolbar.copy(
isVisible = false
),
styleTextToolbar = state.styleTextToolbar.copy(
isVisible = true,
target = target,
style = style,
config = event.target.getStyleConfig(event.focused, event.selection),
props = getPropsForSelection(target, event.selection)
),
navigationToolbar = state.navigationToolbar.copy(
isVisible = false
),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
// TODO move it somewhere in appropriate group
is Event.OnMultiSelectTextStyleClicked -> {
state.copy(
mainToolbar = state.mainToolbar.copy(
isVisible = false
),
styleTextToolbar = state.styleTextToolbar.copy(
isVisible = true,
target = event.target,
style = event.style,
props = event.props,
config = event.config
),
navigationToolbar = state.navigationToolbar.copy(
isVisible = false
),
objectTypesToolbar = Toolbar.ObjectTypes.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
is Event.OnMultiSelectBackgroundStyleClicked -> {
state.copy(
mainToolbar = state.mainToolbar.copy(
isVisible = false
),
styleTextToolbar = state.styleTextToolbar.copy(
isVisible = false
),
navigationToolbar = state.navigationToolbar.copy(
isVisible = false
),
objectTypesToolbar = Toolbar.ObjectTypes.reset(),
styleColorBackgroundToolbar = Toolbar.Styling.ColorBackground(
isVisible = false
),
styleBackgroundToolbar = Toolbar.Styling.Background(
isVisible = true,
selectedBackground = event.selectedBackground
)
)
}
is Event.OnRefresh.StyleToolbar -> {
handleRefreshForMarkupLevelStyling(state, event)
}
is Event.OnRefresh.Markup -> {
if (event.target != null && event.selection != null) {
state.copy(
@ -500,16 +390,6 @@ sealed class ControlPanelMachine {
state.copy()
}
}
is Event.OnRefresh.StyleToolbarMulti -> {
state.copy(
styleTextToolbar = state.styleTextToolbar.copy(
props = event.props,
style = event.style,
target = event.target,
config = event.config
)
)
}
is Event.MultiSelect -> {
handleMultiSelectEvent(event, state)
}
@ -604,120 +484,26 @@ sealed class ControlPanelMachine {
}
}
private fun handleRefreshForMarkupLevelStyling(
state: ControlPanelState,
event: Event.OnRefresh.StyleToolbar
): ControlPanelState {
val target =
event.target?.let {
target(
block = it,
urlBuilder = event.urlBuilder,
details = event.details
)
}
val style = event.target?.let {
val content = it.content
check(content is Block.Content.Text)
content.style
} ?: TextStyle.P
return state.copy(
styleTextToolbar = state.styleTextToolbar.copy(
target = target,
props = target?.let {
Toolbar.Styling.Props(
isBold = it.isBold,
isItalic = it.isItalic,
isStrikethrough = it.isStrikethrough,
isCode = it.isCode,
isLinked = it.isLinked,
color = it.color,
background = it.background,
alignment = it.alignment
)
},
style = style
)
)
}
private fun getPropsForSelection(target: Toolbar.Styling.Target, selection: IntRange?)
: Toolbar.Styling.Props {
return if (selection != null && selection.first != selection.last) {
getMarkupLevelStylingProps(target, selection)
} else {
Toolbar.Styling.Props(
isBold = target.isBold,
isItalic = target.isItalic,
isStrikethrough = target.isStrikethrough,
isCode = target.isCode,
isLinked = target.isLinked,
color = target.color,
background = target.background,
alignment = target.alignment
)
}
}
private fun getMarkupLevelStylingProps(
target: Toolbar.Styling.Target,
selection: IntRange
): Toolbar.Styling.Props {
var color: String? = null
var background: String? = null
val colorOverlaps = mutableListOf<Overlap>()
val backgroundOverlaps = mutableListOf<Overlap>()
target.marks.forEach { mark ->
if (mark is Markup.Mark.TextColor) {
val range = mark.from..mark.to
val overlap = selection.overlap(range)
if (incl.contains(overlap))
color = mark.color
else
colorOverlaps.add(overlap)
} else if (mark is Markup.Mark.BackgroundColor) {
val range = mark.from..mark.to
val overlap = selection.overlap(range)
if (incl.contains(overlap))
background = mark.background
else
backgroundOverlaps.add(overlap)
}
}
if (color == null) {
if (colorOverlaps.isEmpty() || colorOverlaps.none { value -> excl.contains(value) })
color = target.color ?: ThemeColor.DEFAULT.title
}
if (background == null) {
if (backgroundOverlaps.isEmpty() || backgroundOverlaps.none { value ->
excl.contains(
value
)
})
background = target.background ?: ThemeColor.DEFAULT.title
}
return Toolbar.Styling.Props(
isBold = target.marks.isBoldInRange(selection),
isItalic = target.marks.isItalicInRange(selection),
isStrikethrough = target.marks.isStrikethroughInRange(selection),
isCode = target.marks.isKeyboardInRange(selection),
isLinked = target.marks.isLinkInRange(selection),
color = color,
background = background,
alignment = target.alignment
)
}
private fun handleStylingToolbarEvent(
event: Event.StylingToolbar,
state: ControlPanelState
): ControlPanelState = when (event) {
is Event.StylingToolbar.OnUpdateTextToolbar -> {
state.copy(
mainToolbar = state.mainToolbar.copy(
isVisible = false
),
styleTextToolbar = state.styleTextToolbar.copy(
isVisible = true,
state = event.state
),
navigationToolbar = state.navigationToolbar.copy(
isVisible = false
),
objectTypesToolbar = Toolbar.ObjectTypes.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
is Event.StylingToolbar.OnClose -> {
if (event.focused) {
state.copy(
@ -756,21 +542,10 @@ sealed class ControlPanelMachine {
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
is Event.StylingToolbar.OnExtraClicked -> {
is Event.StylingToolbar.OnUpdateOtherToolbar -> {
state.copy(
styleTextToolbar = state.styleTextToolbar.copy(
isVisible = false
),
styleExtraToolbar = Toolbar.Styling.Extra(true),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
is Event.StylingToolbar.OnColorBackgroundClicked -> {
state.copy(
styleTextToolbar = state.styleTextToolbar.copy(
isVisible = false
),
styleColorBackgroundToolbar = Toolbar.Styling.ColorBackground(true),
styleTextToolbar = Toolbar.Styling.reset(),
styleExtraToolbar = Toolbar.Styling.Extra(true, event.state),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
@ -779,7 +554,7 @@ sealed class ControlPanelMachine {
styleTextToolbar = state.styleTextToolbar.copy(
isVisible = true
),
styleExtraToolbar = Toolbar.Styling.Extra(false),
styleExtraToolbar = Toolbar.Styling.Extra.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
@ -788,7 +563,7 @@ sealed class ControlPanelMachine {
styleTextToolbar = state.styleTextToolbar.copy(
isVisible = true
),
styleColorBackgroundToolbar = Toolbar.Styling.ColorBackground(false),
styleColorBackgroundToolbar = Toolbar.Styling.ColorBackground.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
@ -797,6 +572,34 @@ sealed class ControlPanelMachine {
styleBackgroundToolbar = Toolbar.Styling.Background.reset()
)
}
is Event.StylingToolbar.OnUpdateBackgroundToolbar -> {
state.copy(
mainToolbar = state.mainToolbar.copy(
isVisible = false
),
styleTextToolbar = Toolbar.Styling.reset(),
navigationToolbar = state.navigationToolbar.copy(
isVisible = false
),
objectTypesToolbar = Toolbar.ObjectTypes.reset(),
styleColorBackgroundToolbar = Toolbar.Styling.ColorBackground.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background(
isVisible = true,
state = event.state
)
)
}
is Event.StylingToolbar.OnUpdateColorBackgroundToolbar -> {
state.copy(
styleColorBackgroundToolbar = Toolbar.Styling.ColorBackground(
isVisible = true,
state = event.state
),
styleTextToolbar = Toolbar.Styling.reset(),
styleBackgroundToolbar = Toolbar.Styling.Background.reset(),
styleExtraToolbar = Toolbar.Styling.Extra.reset()
)
}
}
private fun handleMentionEvent(
@ -1050,44 +853,6 @@ sealed class ControlPanelMachine {
}
}
//todo Need refactoring
private fun target(
block: Block,
details: Block.Details,
urlBuilder: UrlBuilder
): Toolbar.Styling.Target =
when (val content = block.content) {
is Block.Content.RelationBlock -> {
Toolbar.Styling.Target(
id = block.id,
text = "",
color = null,
background = block.backgroundColor,
alignment = null,
marks = listOf()
)
}
is Block.Content.Text -> {
Toolbar.Styling.Target(
id = block.id,
text = content.text,
color = content.color,
background = block.backgroundColor,
alignment = content.align?.let { alignment ->
when (alignment) {
Block.Align.AlignCenter -> Alignment.CENTER
Block.Align.AlignLeft -> Alignment.START
Block.Align.AlignRight -> Alignment.END
}
},
marks = content.marks(urlBuilder = urlBuilder, details = details)
)
}
else -> {
throw IllegalArgumentException("Unexpected content type for style toolbar: ${block.content::class.java.simpleName}")
}
}
private fun logState(text: String, state: ControlPanelState) {
Timber.i(
"REDUCER, $text STATE:${

View file

@ -30,6 +30,7 @@ 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.isAllTextBlocks
import com.anytypeio.anytype.core_models.ext.parents
import com.anytypeio.anytype.core_models.ext.process
import com.anytypeio.anytype.core_models.ext.sortByType
@ -113,7 +114,6 @@ import com.anytypeio.anytype.presentation.editor.editor.mention.MentionConst.MEN
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionConst.MENTION_TITLE_EMPTY
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionEvent
import com.anytypeio.anytype.presentation.editor.editor.mention.getMentionName
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.Focusable
import com.anytypeio.anytype.presentation.editor.editor.model.UiBlock
@ -133,8 +133,13 @@ import com.anytypeio.anytype.presentation.editor.editor.slash.SlashWidgetState
import com.anytypeio.anytype.presentation.editor.editor.slash.convertToMarkType
import com.anytypeio.anytype.presentation.editor.editor.slash.convertToUiBlock
import com.anytypeio.anytype.presentation.editor.editor.slash.toSlashItemView
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import com.anytypeio.anytype.presentation.editor.editor.styling.getIds
import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleBackgroundToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleColorBackgroundToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleOtherToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.getStyleTextToolbarState
import com.anytypeio.anytype.presentation.editor.editor.toCoreModel
import com.anytypeio.anytype.presentation.editor.editor.updateText
import com.anytypeio.anytype.presentation.editor.model.EditorFooter
import com.anytypeio.anytype.presentation.editor.model.TextUpdate
@ -163,9 +168,6 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsSetDescriptionE
import com.anytypeio.anytype.presentation.extension.sendAnalyticsSetTitleEvent
import com.anytypeio.anytype.presentation.extension.sendAnalyticsUpdateTextMarkupEvent
import com.anytypeio.anytype.presentation.extension.sendAnalyticsWritingEvent
import com.anytypeio.anytype.presentation.mapper.getPropsForSelectedTextBlocks
import com.anytypeio.anytype.presentation.mapper.getSelectedBackgroundForSelectedBlocks
import com.anytypeio.anytype.presentation.mapper.getTextStyleForSelectedTextBlocks
import com.anytypeio.anytype.presentation.mapper.mark
import com.anytypeio.anytype.presentation.mapper.style
import com.anytypeio.anytype.presentation.mapper.toObjectTypeView
@ -690,47 +692,15 @@ class EditorViewModel(
private fun refreshStyleToolbar(document: Document) {
controlPanelViewState.value?.let { state ->
if (state.styleTextToolbar.isVisible || state.styleColorBackgroundToolbar.isVisible) {
when (mode) {
is EditorMode.Styling.Multi -> {
val ids = (mode as EditorMode.Styling.Multi).targets
val selected = blocks.filter { ids.contains(it.id) }
controlPanelInteractor.onEvent(
event = ControlPanelMachine.Event.OnRefresh.StyleToolbarMulti(
target = null,
config = StyleConfig.emptyState(),
props = selected.getPropsForSelectedTextBlocks(),
style = selected.getTextStyleForSelectedTextBlocks()
)
)
}
is EditorMode.Styling.Single -> {
state.styleTextToolbar.target?.id?.let { targetId ->
controlPanelInteractor.onEvent(
event = ControlPanelMachine.Event.OnRefresh.StyleToolbar(
target = document.find { it.id == targetId },
selection = orchestrator.stores.textSelection.current().selection,
urlBuilder = urlBuilder,
details = orchestrator.stores.details.current()
)
)
}
}
else -> {
Timber.e("refreshStyleToolbar, wrong mode:$mode, skip refresh")
}
}
if (state.styleTextToolbar.isVisible) {
val ids = mode.getIds()
if (ids.isNullOrEmpty()) return
onSendRefreshStyleTextToolbarEvent(ids)
}
if (state.styleBackgroundToolbar.isVisible) {
if (mode is EditorMode.Styling.Multi) {
val ids = (mode as EditorMode.Styling.Multi).targets
val selected = blocks.filter { ids.contains(it.id) }
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.OnMultiSelectBackgroundStyleClicked(
selectedBackground = selected.getSelectedBackgroundForSelectedBlocks()
)
)
}
val ids = mode.getIds()
if (ids.isNullOrEmpty()) return
onSendRefreshStyleBackgroundToolbarEvent(ids)
}
if (state.markupMainToolbar.isVisible) {
controlPanelInteractor.onEvent(
@ -740,9 +710,54 @@ class EditorViewModel(
)
)
}
if (state.styleColorBackgroundToolbar.isVisible) {
val ids = mode.getIds()
if (ids.isNullOrEmpty()) return
onSendUpdateStyleColorBackgroundToolbarEvent(ids)
}
if (state.styleExtraToolbar.isVisible) {
val ids = mode.getIds()
if (ids.isNullOrEmpty()) return
onSendUpdateStyleOtherToolbarEvent(ids)
}
}
}
private fun onSendRefreshStyleTextToolbarEvent(ids: List<Id>) {
val selected = blocks.filter { ids.contains(it.id) }
val isAllSelectedText = selected.isAllTextBlocks()
if (isAllSelectedText) {
val state = selected.map { it.content.asText() }.getStyleTextToolbarState()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.StylingToolbar.OnUpdateTextToolbar(state)
)
}
}
private fun onSendRefreshStyleBackgroundToolbarEvent(ids: List<Id>) {
val selected = blocks.filter { ids.contains(it.id) }
val state = selected.getStyleBackgroundToolbarState()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.StylingToolbar.OnUpdateBackgroundToolbar(state)
)
}
private fun onSendUpdateStyleColorBackgroundToolbarEvent(ids: List<Id>) {
val selected = blocks.filter { ids.contains(it.id) }
val state = selected.getStyleColorBackgroundToolbarState()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.StylingToolbar.OnUpdateColorBackgroundToolbar(state)
)
}
private fun onSendUpdateStyleOtherToolbarEvent(ids: List<Id>) {
val selected = blocks.filter { ids.contains(it.id) }
val state = selected.map { it.content.asText() }.getStyleOtherToolbarState()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.StylingToolbar.OnUpdateOtherToolbar(state)
)
}
private fun dispatchToUI(views: List<BlockView>) {
stateData.postValue(
ViewState.Success(
@ -1625,148 +1640,47 @@ class EditorViewModel(
fun onStylingToolbarEvent(event: StylingEvent) {
Timber.d("onStylingToolbarEvent, event:[$event]")
val state = controlPanelViewState.value!!
val ids: List<Id>? = mode.getIds()
if (ids.isNullOrEmpty()) return
when (event) {
is StylingEvent.Coloring.Text -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
onToolbarTextColorAction(currentMode.targets.toList(), event.color.title)
} else {
proceedWithStylingEvent(state, Markup.Type.TEXT_COLOR, event.color.title)
}
viewModelScope.sendAnalyticsUpdateTextMarkupEvent(
analytics = analytics,
type = Content.Text.Mark.Type.TEXT_COLOR,
context = analyticsContext
)
onToolbarTextColorAction(ids, event.color.title)
}
is StylingEvent.Coloring.Background -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
onBlockBackgroundColorAction(currentMode.targets.toList(), event.color.title)
viewModelScope.sendAnalyticsBlockBackgroundEvent(
analytics = analytics,
count = currentMode.targets.size,
color = event.color.title,
context = analyticsContext
)
} else {
proceedWithStylingEvent(state, Markup.Type.BACKGROUND_COLOR, event.color.title)
viewModelScope.sendAnalyticsBlockBackgroundEvent(
analytics = analytics,
count = 1,
color = event.color.title,
context = analyticsContext
)
}
onBlockBackgroundColorAction(ids, event.color.title)
}
is StylingEvent.Markup.Bold -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
sendToast(ERROR_UNSUPPORTED_BEHAVIOR)
} else {
proceedWithStylingEvent(state, Markup.Type.BOLD, null)
}
viewModelScope.sendAnalyticsUpdateTextMarkupEvent(
analytics = analytics,
type = Content.Text.Mark.Type.BOLD,
context = analyticsContext
)
onUpdateBlockListMarkup(ids, Markup.Type.BOLD)
}
is StylingEvent.Markup.Italic -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
sendToast(ERROR_UNSUPPORTED_BEHAVIOR)
} else {
proceedWithStylingEvent(state, Markup.Type.ITALIC, null)
}
viewModelScope.sendAnalyticsUpdateTextMarkupEvent(
analytics = analytics,
type = Content.Text.Mark.Type.ITALIC,
context = analyticsContext
)
onUpdateBlockListMarkup(ids, Markup.Type.ITALIC)
}
is StylingEvent.Markup.StrikeThrough -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
sendToast(ERROR_UNSUPPORTED_BEHAVIOR)
} else {
proceedWithStylingEvent(state, Markup.Type.STRIKETHROUGH, null)
}
onUpdateBlockListMarkup(ids, Markup.Type.STRIKETHROUGH)
}
is StylingEvent.Markup.Code -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
sendToast(ERROR_UNSUPPORTED_BEHAVIOR)
} else {
proceedWithStylingEvent(state, Markup.Type.KEYBOARD, null)
}
onUpdateBlockListMarkup(ids, Markup.Type.KEYBOARD)
}
is StylingEvent.Markup.Link -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
sendToast(ERROR_UNSUPPORTED_BEHAVIOR)
if (ids.size == 1) {
onBlockStyleLinkClicked(ids[0])
} else {
proceedWithStylingEvent(state, Markup.Type.LINK, null)
sendToast(ERROR_UNSUPPORTED_BEHAVIOR)
}
}
is StylingEvent.Alignment.Left -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
proceedWithAlignmentUpdate(
targets = currentMode.targets.toList(),
alignment = Block.Align.AlignLeft
)
} else {
onBlockAlignmentActionClicked(Alignment.START)
}
proceedWithAlignmentUpdate(ids, Block.Align.AlignLeft)
}
is StylingEvent.Alignment.Center -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
proceedWithAlignmentUpdate(
targets = currentMode.targets.toList(),
alignment = Block.Align.AlignCenter
)
} else {
onBlockAlignmentActionClicked(Alignment.CENTER)
}
proceedWithAlignmentUpdate(ids, Block.Align.AlignCenter)
}
is StylingEvent.Alignment.Right -> {
val currentMode = mode
if (currentMode is EditorMode.Styling.Multi) {
proceedWithAlignmentUpdate(
targets = currentMode.targets.toList(),
alignment = Block.Align.AlignRight
)
} else {
onBlockAlignmentActionClicked(Alignment.END)
}
proceedWithAlignmentUpdate(ids, Block.Align.AlignRight)
}
else -> Timber.d("Ignoring styling toolbar event: $event")
}
}
private fun proceedWithStylingEvent(
state: ControlPanelState,
type: Markup.Type,
param: String?
) {
state.styleTextToolbar.target?.id?.let { id ->
when (type) {
Markup.Type.ITALIC -> onBlockStyleMarkupActionClicked(id, type)
Markup.Type.BOLD -> onBlockStyleMarkupActionClicked(id, type)
Markup.Type.STRIKETHROUGH -> onBlockStyleMarkupActionClicked(id, type)
Markup.Type.TEXT_COLOR -> onToolbarTextColorAction(listOf(id), param)
Markup.Type.BACKGROUND_COLOR -> onBlockBackgroundColorAction(listOf(id), param)
Markup.Type.LINK -> onBlockStyleLinkClicked(id)
Markup.Type.KEYBOARD -> onBlockStyleMarkupActionClicked(id, type)
Markup.Type.MENTION -> Unit
Markup.Type.OBJECT -> Unit
}
} ?: run { Timber.e("Target id was missing for markup styling event: $type") }
}
fun onStyleToolbarMarkupAction(type: Markup.Type, param: String? = null) {
Timber.d("onStyleToolbarMarkupAction, type:[$type] param:[$param]")
viewModelScope.launch {
@ -1784,19 +1698,6 @@ class EditorViewModel(
)
}
private fun onBlockAlignmentActionClicked(alignment: Alignment) {
controlPanelViewState.value?.styleTextToolbar?.target?.id?.let { id ->
proceedWithAlignmentUpdate(
targets = listOf(id),
alignment = when (alignment) {
Alignment.START -> Block.Align.AlignLeft
Alignment.CENTER -> Block.Align.AlignCenter
Alignment.END -> Block.Align.AlignRight
}
)
}
}
private fun proceedWithAlignmentUpdate(targets: List<Id>, alignment: Block.Align) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
@ -1828,20 +1729,29 @@ class EditorViewModel(
)
)
}
viewModelScope.sendAnalyticsUpdateTextMarkupEvent(
analytics = analytics,
type = Content.Text.Mark.Type.TEXT_COLOR,
context = analyticsContext
)
}
private fun onBlockBackgroundColorAction(targets: List<Id>, color: String?) {
check(color != null)
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnBlockBackgroundColorSelected)
private fun onBlockBackgroundColorAction(ids: List<Id>, color: String) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.Text.UpdateBackgroundColor(
context = context,
targets = targets,
targets = ids,
color = color
)
)
}
viewModelScope.sendAnalyticsBlockBackgroundEvent(
analytics = analytics,
count = ids.size,
color = color,
context = analyticsContext
)
}
private fun onBlockStyleLinkClicked(id: String) {
@ -1853,44 +1763,23 @@ class EditorViewModel(
stateData.value = ViewState.OpenLinkScreen(context, target, range)
}
private fun onBlockStyleMarkupActionClicked(id: String, action: Markup.Type) {
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.OnBlockStyleSelected
)
val target = blocks.first { it.id == id }
val content = target.content as Content.Text
if (content.text.isNotEmpty()) {
val new = target.markup(
type = action,
range = 0..content.text.length,
param = null
)
val update = blocks.map { block ->
if (block.id != target.id)
block
else
new
}
orchestrator.stores.document.update(update)
viewModelScope.launch { refresh() }
viewModelScope.launch {
proceedWithUpdatingText(
intent = Intent.Text.UpdateText(
context = context,
target = new.id,
text = new.content<Content.Text>().text,
marks = new.content<Content.Text>().marks
private fun onUpdateBlockListMarkup(ids: List<Id>, type: Markup.Type) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Intent.Text.UpdateMark(
context = context,
targets = ids,
mark = Content.Text.Mark(
range = IntRange(0, Int.MAX_VALUE),
type = type.toCoreModel()
)
)
}
)
sendAnalyticsUpdateTextMarkupEvent(
analytics = analytics,
type = type,
context = analyticsContext
)
}
}
@ -2310,7 +2199,8 @@ class EditorViewModel(
private fun proceedWithStyleToolbarEvent() {
val target = orchestrator.stores.focus.current().id
val targetBlock = blocks.find { it.id == target }
if (targetBlock != null) {
val isText = targetBlock?.content is Content.Text
if (targetBlock != null && isText) {
mode = EditorMode.Styling.Single(
target = target,
cursor = orchestrator.stores.textSelection.current().selection?.first
@ -2321,41 +2211,37 @@ class EditorViewModel(
renderCommand.send(Unit)
}
val textSelection = orchestrator.stores.textSelection.current()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.OnBlockActionToolbarStyleClicked(
target = targetBlock,
focused = textSelection.isNotEmpty,
selection = textSelection.selection,
urlBuilder = urlBuilder,
details = orchestrator.stores.details.current()
if ((targetBlock.content as Content.Text).style == Content.Text.Style.CODE_SNIPPET) {
val state = listOf(targetBlock).getStyleBackgroundToolbarState()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.StylingToolbar.OnUpdateBackgroundToolbar(state)
)
)
} else {
val styleState =
listOf(targetBlock).map { it.content.asText() }.getStyleTextToolbarState()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.StylingToolbar.OnUpdateTextToolbar(styleState)
)
}
} else {
Timber.e("Target block for style menu not found. Target id: $target")
sendToast("Target block for style menu not found.")
sendToast("Couldn't show style menu")
}
}
private fun proceedWithMultiStyleToolbarEvent() {
val selected = blocks.filter { currentSelection().contains(it.id) }
val isAllSelectedText = selected.isAllTextAndNoneCodeBlocks()
val isAllTextAndNoneCodeBlocks = selected.isAllTextAndNoneCodeBlocks()
mode = EditorMode.Styling.Multi(currentSelection())
if (isAllSelectedText) {
if (isAllTextAndNoneCodeBlocks) {
val styleState = selected.map { it.content.asText() }.getStyleTextToolbarState()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.OnMultiSelectTextStyleClicked(
target = null,
config = StyleConfig.emptyState(),
props = selected.getPropsForSelectedTextBlocks(),
style = selected.getTextStyleForSelectedTextBlocks()
)
ControlPanelMachine.Event.StylingToolbar.OnUpdateTextToolbar(styleState)
)
} else {
val styleState = selected.getStyleBackgroundToolbarState()
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.OnMultiSelectBackgroundStyleClicked(
selectedBackground = selected.getSelectedBackgroundForSelectedBlocks()
)
ControlPanelMachine.Event.StylingToolbar.OnUpdateBackgroundToolbar(styleState)
)
}
}
@ -2448,9 +2334,7 @@ class EditorViewModel(
fun onCloseBlockStyleBackgroundToolbarClicked() {
Timber.d("onCloseBlockStyleColorToolbarClicked, ")
controlPanelInteractor.onEvent(
ControlPanelMachine.Event.StylingToolbar.OnBackgroundClosed
)
onCloseBlockStyleToolbarClicked()
}
fun onBlockToolbarBlockActionsClicked() {
@ -2527,30 +2411,27 @@ class EditorViewModel(
fun onUpdateTextBlockStyle(uiBlock: UiBlock) {
Timber.d("onUpdateSingleTextBlockStyle, uiBlock:[$uiBlock]")
(mode as? EditorMode.Styling.Single)?.let { eMode ->
proceedUpdateBlockStyle(
targets = listOf(eMode.target),
uiBlock = uiBlock,
errorAction = { sendToast("Cannot convert block to $uiBlock") }
)
}
(mode as? EditorMode.Styling.Multi)?.let {
proceedUpdateBlockStyle(
targets = currentSelection().toList(),
uiBlock = uiBlock,
errorAction = { sendToast("Cannot convert block to $uiBlock") }
)
}
val ids = mode.getIds()
if (ids.isNullOrEmpty()) return
proceedUpdateBlockStyle(
targets = ids,
uiBlock = uiBlock,
errorAction = { sendToast("Cannot convert block to $uiBlock") }
)
}
fun onBlockStyleToolbarOtherClicked() {
Timber.d("onBlockStyleToolbarOtherClicked, ")
controlPanelInteractor.onEvent(ControlPanelMachine.Event.StylingToolbar.OnExtraClicked)
val ids = mode.getIds()
if (ids.isNullOrEmpty()) return
onSendUpdateStyleOtherToolbarEvent(ids)
}
fun onBlockStyleToolbarColorClicked() {
Timber.d("onBlockStyleToolbarColorClicked, ")
controlPanelInteractor.onEvent(ControlPanelMachine.Event.StylingToolbar.OnColorBackgroundClicked)
val ids = mode.getIds()
if (ids.isNullOrEmpty()) return
onSendUpdateStyleColorBackgroundToolbarEvent(ids)
}
private fun proceedUpdateBlockStyle(

View file

@ -29,23 +29,25 @@ fun Block.markup(
val new = Mark(
range = range,
type = when (type) {
Markup.Type.BOLD -> Mark.Type.BOLD
Markup.Type.ITALIC -> Mark.Type.ITALIC
Markup.Type.STRIKETHROUGH -> Mark.Type.STRIKETHROUGH
Markup.Type.TEXT_COLOR -> Mark.Type.TEXT_COLOR
Markup.Type.LINK -> Mark.Type.LINK
Markup.Type.BACKGROUND_COLOR -> Mark.Type.BACKGROUND_COLOR
Markup.Type.KEYBOARD -> Mark.Type.KEYBOARD
Markup.Type.MENTION -> Mark.Type.MENTION
Markup.Type.OBJECT -> Mark.Type.OBJECT
},
type =type.toCoreModel(),
param = param
)
return copy(content = content.addMarkToContent(new))
}
fun Markup.Type.toCoreModel(): Mark.Type = when (this) {
Markup.Type.BOLD -> Mark.Type.BOLD
Markup.Type.ITALIC -> Mark.Type.ITALIC
Markup.Type.STRIKETHROUGH -> Mark.Type.STRIKETHROUGH
Markup.Type.TEXT_COLOR -> Mark.Type.TEXT_COLOR
Markup.Type.LINK -> Mark.Type.LINK
Markup.Type.BACKGROUND_COLOR -> Mark.Type.BACKGROUND_COLOR
Markup.Type.KEYBOARD -> Mark.Type.KEYBOARD
Markup.Type.MENTION -> Mark.Type.MENTION
Markup.Type.OBJECT -> Mark.Type.OBJECT
}
fun Block.Content.Text.addMarkToContent(mark: Mark): Block.Content.Text {
return if (mark.isClickableMark()) {
this.copy(marks = marks.addClickableMark(mark).sortByType())

View file

@ -1,10 +1,8 @@
package com.anytypeio.anytype.presentation.editor.editor.control
import com.anytypeio.anytype.core_models.TextStyle
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.slash.SlashWidgetState
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.markup.MarkupStyleDescriptor
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
@ -103,85 +101,47 @@ data class ControlPanelState(
*/
data class Styling(
override val isVisible: Boolean,
val target: Target? = null,
val config: StyleConfig? = null,
val props: Props? = null,
val style: TextStyle? = TextStyle.P
val state: StyleToolbarState.Text
) : Toolbar() {
companion object {
fun reset() = Styling(
isVisible = false,
target = null,
props = null,
state = StyleToolbarState.Text.empty()
)
}
data class Extra(override val isVisible: Boolean) : Toolbar() {
data class Extra(
override val isVisible: Boolean,
val state: StyleToolbarState.Other
) : Toolbar() {
companion object {
fun reset() = Extra(false)
fun reset() = Extra(
isVisible = false,
state = StyleToolbarState.Other.empty()
)
}
}
data class ColorBackground(override val isVisible: Boolean = false) : Toolbar() {
data class ColorBackground(
override val isVisible: Boolean = false,
val state: StyleToolbarState.ColorBackground
) : Toolbar() {
companion object {
fun reset() = ColorBackground(false)
fun reset() = ColorBackground(
isVisible = false,
state = StyleToolbarState.ColorBackground.empty()
)
}
}
data class Background(
override val isVisible: Boolean,
val selectedBackground: String?
val state: StyleToolbarState.Background
) : Toolbar() {
companion object {
fun reset() = Background(isVisible = false, selectedBackground = null)
}
}
/**
* Target's properties corresponding to current selection or styling mode.
*/
data class Props(
val isBold: Boolean = false,
val isItalic: Boolean = false,
val isStrikethrough: Boolean = false,
val isCode: Boolean = false,
val isLinked: Boolean = false,
val color: String? = null,
val background: String? = null,
val alignment: Alignment? = null
)
/**
* Target block associated with this toolbar.
*/
data class Target(
val id: String,
val text: String,
val color: String?,
val background: String?,
val alignment: Alignment?,
val marks: List<Markup.Mark>
) {
val isBold: Boolean = marks.any { mark ->
mark is Markup.Mark.Bold && mark.from == 0 && mark.to == text.length
}
val isItalic: Boolean = marks.any { mark ->
mark is Markup.Mark.Italic && mark.from == 0 && mark.to == text.length
}
val isStrikethrough: Boolean = marks.any { mark ->
mark is Markup.Mark.Strikethrough && mark.from == 0 && mark.to == text.length
}
val isCode: Boolean = marks.any { mark ->
mark is Markup.Mark.Keyboard && mark.from == 0 && mark.to == text.length
}
val isLinked: Boolean = marks.any { mark ->
mark is Markup.Mark.Link && mark.from == 0 && mark.to == text.length
fun reset() =
Background(isVisible = false, state = StyleToolbarState.Background.empty())
}
}
}
@ -312,7 +272,8 @@ data class ControlPanelState(
count = 0
),
styleTextToolbar = Toolbar.Styling(
isVisible = false
isVisible = false,
state = StyleToolbarState.Text.empty()
),
mentionToolbar = Toolbar.MentionToolbar(
isVisible = false,

View file

@ -387,7 +387,12 @@ fun List<BlockView>.updateCursorAndEditMode(
isFocused = isTarget,
cursor = if (isTarget) cursor else null
)
is BlockView.Code -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Code -> view.copy(
mode = BlockView.Mode.EDIT,
isSelected = false,
isFocused = isTarget,
cursor = if (isTarget) cursor else null
)
is BlockView.Error.File -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Error.Video -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Error.Picture -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)

View file

@ -580,10 +580,11 @@ sealed class BlockView : ViewType {
override var isFocused: Boolean = false,
override val isSelected: Boolean = false,
override val color: String? = null,
override var cursor: Int? = null,
override val backgroundColor: String? = null,
override val indent: Int = 0,
val lang: String? = null
) : BlockView(), Permission, Selectable, Focusable, Indentable, TextSupport {
) : BlockView(), Permission, Selectable, Focusable, Cursor, Indentable, TextSupport {
override fun getViewType() = HOLDER_CODE_SNIPPET
}

View file

@ -1,345 +0,0 @@
package com.anytypeio.anytype.presentation.editor.editor.styling
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
data class StyleConfig(
val visibleTypes: List<StylingType>,
val enabledMarkup: List<Markup.Type>,
val enabledAlignment: List<Alignment>
) {
companion object {
fun emptyState() = StyleConfig(
visibleTypes = emptyList(),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
}
}
fun Block.getStyleConfig(focus: Boolean?, selection: IntRange?): StyleConfig =
when (val cnt = content) {
is Block.Content.Text -> cnt.getTextStyleConfig(focus, selection)
is Block.Content.Link -> cnt.getLinkStyleConfig()
is Block.Content.File -> cnt.getFileStyleConfig(focus)
is Block.Content.Bookmark -> cnt.getBookmarkStyleConfig(focus)
is Block.Content.RelationBlock -> cnt.getRelationStyleConfig()
else -> StyleConfig.emptyState()
}
fun Block.Content.Bookmark.getBookmarkStyleConfig(focus: Boolean?): StyleConfig {
check(focus == null) { "Bookmark block should has null focus" }
return getStyleConfig()
}
fun Block.Content.Bookmark.getStyleConfig(): StyleConfig = StyleConfig.emptyState()
fun Block.Content.Link.getLinkStyleConfig(): StyleConfig = StyleConfig(
visibleTypes = listOf(StylingType.BACKGROUND),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
fun Block.Content.RelationBlock.getRelationStyleConfig(): StyleConfig = StyleConfig(
visibleTypes = listOf(StylingType.BACKGROUND),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
fun Block.Content.File.getFileStyleConfig(focus: Boolean?): StyleConfig {
check(focus == null) { "File block should has null focus" }
return getStyleConfig()
}
fun Block.Content.File.getStyleConfig(): StyleConfig = when (type) {
Block.Content.File.Type.FILE -> {
StyleConfig(
visibleTypes = listOf(StylingType.BACKGROUND),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
}
Block.Content.File.Type.IMAGE -> {
StyleConfig(
visibleTypes = listOf(StylingType.BACKGROUND),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
}
Block.Content.File.Type.VIDEO -> {
StyleConfig(
visibleTypes = listOf(StylingType.BACKGROUND),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
}
else -> StyleConfig.emptyState()
}
fun Block.Content.Text.getTextStyleConfig(focus: Boolean?, selection: IntRange?): StyleConfig {
return if (focus != null && focus) {
getStyleConfig(selection = selection)
} else {
getStyleConfig(selection = null)
}
}
fun Block.Content.Text.getStyleConfig(selection: IntRange? = null): StyleConfig {
return if (selection == null || selection.first >= selection.last) {
getBlockStyle(style)
} else {
getMarkupStyle(style)
}
}
fun Block.Content.Text.getSupportedMarkupTypes() : List<Markup.Type> = when(style) {
Block.Content.Text.Style.P -> {
listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
}
Block.Content.Text.Style.H1, Block.Content.Text.Style.H2,
Block.Content.Text.Style.H3, Block.Content.Text.Style.H4 -> {
listOf(
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
}
Block.Content.Text.Style.QUOTE -> {
listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
}
Block.Content.Text.Style.BULLET, Block.Content.Text.Style.NUMBERED,
Block.Content.Text.Style.TOGGLE, Block.Content.Text.Style.CHECKBOX -> {
listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
}
else -> emptyList()
}
fun Block.Content.Text.getBlockStyle(style: Block.Content.Text.Style) = when (style) {
Block.Content.Text.Style.P -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = listOf(Alignment.START, Alignment.CENTER, Alignment.END)
)
}
Block.Content.Text.Style.H1, Block.Content.Text.Style.H2,
Block.Content.Text.Style.H3, Block.Content.Text.Style.H4 -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = listOf(Alignment.START, Alignment.CENTER, Alignment.END)
)
}
Block.Content.Text.Style.TITLE -> {
StyleConfig(
visibleTypes = listOf(StylingType.BACKGROUND),
enabledMarkup = emptyList(),
enabledAlignment = listOf(Alignment.START, Alignment.CENTER, Alignment.END)
)
}
Block.Content.Text.Style.QUOTE -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = listOf(Alignment.START, Alignment.END)
)
}
Block.Content.Text.Style.CODE_SNIPPET -> {
StyleConfig(
visibleTypes = listOf(StylingType.BACKGROUND),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.BULLET, Block.Content.Text.Style.NUMBERED,
Block.Content.Text.Style.TOGGLE, Block.Content.Text.Style.CHECKBOX -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.CALLOUT -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.DESCRIPTION -> throw IllegalStateException("Description block does not support styling")
}
fun Block.Content.Text.getMarkupStyle(style: Block.Content.Text.Style) = when (style) {
Block.Content.Text.Style.P -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.H1, Block.Content.Text.Style.H2,
Block.Content.Text.Style.H3, Block.Content.Text.Style.H4 -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.TITLE -> {
StyleConfig(
visibleTypes = listOf(StylingType.BACKGROUND),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.QUOTE -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.CODE_SNIPPET -> {
StyleConfig(
visibleTypes = emptyList(),
enabledMarkup = emptyList(),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.BULLET, Block.Content.Text.Style.NUMBERED,
Block.Content.Text.Style.TOGGLE, Block.Content.Text.Style.CHECKBOX -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.CALLOUT -> {
StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = emptyList()
)
}
Block.Content.Text.Style.DESCRIPTION -> throw IllegalStateException("Description block does not support styling")
}

View file

@ -0,0 +1,165 @@
package com.anytypeio.anytype.presentation.editor.editor.styling
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import timber.log.Timber
fun Editor.Mode.getIds(): List<Id>? = when (this) {
is Editor.Mode.Styling.Multi -> targets.toList()
is Editor.Mode.Styling.Single -> listOf(target)
else -> {
Timber.e("Couldn't get ids of selected blocks, wrong Editor Mode : $this")
null
}
}
/**
* Checks that all block text contents have the same value of style and returns state with this value
* otherwise returns state with null
*/
fun List<Block.Content.Text>.getStyleTextToolbarState(): StyleToolbarState.Text {
val result = map { it.style }.distinct()
return if (result.size == 1) {
StyleToolbarState.Text(
textStyle = result[0]
)
} else {
StyleToolbarState.Text(
textStyle = null
)
}
}
/**
* Checks that all blocks have the same value of background color and returns state with this value
* otherwise returns state with null
* We take as a statement that if block background value is null, then this block have default color
*/
fun List<Block>.getStyleBackgroundToolbarState(): StyleToolbarState.Background {
val result = map { it.backgroundColor ?: ThemeColor.DEFAULT.title }.distinct()
return if (result.size == 1) {
StyleToolbarState.Background(
background = result[0]
)
} else {
StyleToolbarState.Background(
background = null
)
}
}
/**
* Checks that all blocks have the same value of background color and text color and returns state with this values
* otherwise returns state with null
* We take as a statement that if the block's background value is null
* or the block's text color is null, then this block has default background or text colors
*/
fun List<Block>.getStyleColorBackgroundToolbarState(): StyleToolbarState.ColorBackground {
val isAllText = all { it.content is Block.Content.Text }
if (!isAllText) return StyleToolbarState.ColorBackground.empty()
val resultColor = map { it.content.asText().color ?: ThemeColor.DEFAULT.title }.distinct()
val resultBackground = map { it.backgroundColor ?: ThemeColor.DEFAULT.title }.distinct()
return StyleToolbarState.ColorBackground(
background = if (resultBackground.size == 1) resultBackground[0] else null,
color = if (resultColor.size == 1) resultColor[0] else null
)
}
fun List<Block.Content.Text>.getStyleOtherToolbarState(): StyleToolbarState.Other {
val isSupportBold = all { it.isSupportBold() }
val isBoldSelected = if (isSupportBold) {
all { it.isBold() }
} else false
return StyleToolbarState.Other(
isSupportBold = isSupportBold,
isSupportItalic = true,
isSupportCode = true,
isSupportLinked = true,
isSupportStrikethrough = true,
isSupportAlignStart = all { it.alignmentSupport().contains(Alignment.START) },
isSupportAlignCenter = all { it.alignmentSupport().contains(Alignment.CENTER) },
isSupportAlignEnd = all { it.alignmentSupport().contains(Alignment.END) },
isBoldSelected = isBoldSelected,
isItalicSelected = all { it.isItalic() },
isStrikethroughSelected = all { it.isStrikethrough() },
isCodeSelected = all { it.isCode() },
isLinkedSelected = all { it.isLinked() },
isAlignCenterSelected = all { it.isAlignCenter() },
isAlignStartSelected = all { it.isAlignStart() },
isAlignEndSelected = all { it.isAlignEnd() }
)
}
private fun Block.Content.Text.isSupportBold(): Boolean = when (this.style) {
Block.Content.Text.Style.H1,
Block.Content.Text.Style.H2,
Block.Content.Text.Style.H3,
Block.Content.Text.Style.H4,
Block.Content.Text.Style.TITLE,
Block.Content.Text.Style.CODE_SNIPPET -> false
else -> true
}
private fun Block.Content.Text.alignmentSupport(): List<Alignment> = when (this.style) {
Block.Content.Text.Style.P,
Block.Content.Text.Style.H1,
Block.Content.Text.Style.H2,
Block.Content.Text.Style.H3,
Block.Content.Text.Style.H4,
Block.Content.Text.Style.TITLE -> listOf(Alignment.START, Alignment.CENTER, Alignment.END)
Block.Content.Text.Style.QUOTE -> listOf(Alignment.START, Alignment.END)
else -> emptyList()
}
fun Block.Content.Text.getSupportedMarkupTypes(): List<Markup.Type> = when (style) {
Block.Content.Text.Style.P, Block.Content.Text.Style.QUOTE,
Block.Content.Text.Style.BULLET, Block.Content.Text.Style.NUMBERED,
Block.Content.Text.Style.TOGGLE, Block.Content.Text.Style.CHECKBOX -> {
listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
}
Block.Content.Text.Style.H1, Block.Content.Text.Style.H2,
Block.Content.Text.Style.H3, Block.Content.Text.Style.H4 -> {
listOf(
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
}
else -> emptyList()
}
fun Block.Content.Text.isBold(): Boolean = marks.any { mark ->
mark.type == Block.Content.Text.Mark.Type.BOLD && mark.range.first == 0 && mark.range.last == text.length
}
fun Block.Content.Text.isItalic(): Boolean = marks.any { mark ->
mark.type == Block.Content.Text.Mark.Type.ITALIC && mark.range.first == 0 && mark.range.last == text.length
}
fun Block.Content.Text.isStrikethrough(): Boolean = marks.any { mark ->
mark.type == Block.Content.Text.Mark.Type.STRIKETHROUGH && mark.range.first == 0 && mark.range.last == text.length
}
fun Block.Content.Text.isCode(): Boolean = marks.any { mark ->
mark.type == Block.Content.Text.Mark.Type.KEYBOARD && mark.range.first == 0 && mark.range.last == text.length
}
fun Block.Content.Text.isLinked(): Boolean = marks.any { mark ->
mark.type == Block.Content.Text.Mark.Type.LINK && mark.range.first == 0 && mark.range.last == text.length
}
fun Block.Content.Text.isAlignStart(): Boolean = this.align == Block.Align.AlignLeft
fun Block.Content.Text.isAlignCenter(): Boolean = this.align == Block.Align.AlignCenter
fun Block.Content.Text.isAlignEnd(): Boolean = this.align == Block.Align.AlignRight

View file

@ -0,0 +1,48 @@
package com.anytypeio.anytype.presentation.editor.editor.styling
import com.anytypeio.anytype.core_models.TextStyle
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
sealed class StyleToolbarState {
data class Text(val textStyle: TextStyle?) : StyleToolbarState() {
companion object {
fun empty() = Text(null)
}
}
data class Other(
val isSupportBold: Boolean = false,
val isSupportItalic: Boolean = false,
val isSupportStrikethrough: Boolean = false,
val isSupportCode: Boolean = false,
val isSupportLinked: Boolean = false,
val isSupportAlignStart: Boolean = false,
val isSupportAlignCenter: Boolean = false,
val isSupportAlignEnd: Boolean = false,
val isBoldSelected: Boolean = false,
val isItalicSelected: Boolean = false,
val isStrikethroughSelected: Boolean = false,
val isCodeSelected: Boolean = false,
val isLinkedSelected: Boolean = false,
val isAlignStartSelected: Boolean = false,
val isAlignCenterSelected: Boolean = false,
val isAlignEndSelected: Boolean = false
) : StyleToolbarState() {
companion object {
fun empty() = Other()
}
}
data class ColorBackground(val color: String?, val background: String?) : StyleToolbarState() {
companion object {
fun empty() = ColorBackground(null, null)
}
}
data class Background(val background: String?) : StyleToolbarState() {
companion object {
fun empty() = Background(null)
}
}
}

View file

@ -727,20 +727,6 @@ fun ObjectLayoutView.toObjectLayout() = when (this) {
is ObjectLayoutView.Todo -> ObjectType.Layout.TODO
}
fun List<Block>.getPropsForSelectedTextBlocks(): ControlPanelState.Toolbar.Styling.Props {
val colors = map { it.textColor() ?: ThemeColor.DEFAULT.title }.distinct()
val selectedColor = if (colors.size == 1) colors[0] else null
return ControlPanelState.Toolbar.Styling.Props(
color = selectedColor,
background = this.getSelectedBackgroundForSelectedBlocks()
)
}
fun List<Block>.getSelectedBackgroundForSelectedBlocks(): String? {
val backgrounds = map { it.backgroundColor ?: ThemeColor.DEFAULT.title }.distinct()
return if (backgrounds.size == 1) backgrounds[0] else null
}
fun List<Block>.getTextStyleForSelectedTextBlocks(): Block.Content.Text.Style? {
val styles = map { it.textStyle() }.distinct()
return if (styles.size == 1) styles[0] else null

View file

@ -52,7 +52,8 @@ object MockBlockFactory {
fun makeOnePageWithOneTextBlock(
root: String,
child: String,
style: Block.Content.Text.Style = Block.Content.Text.Style.P
style: Block.Content.Text.Style = Block.Content.Text.Style.P,
backgroundColor: String? = null
) = listOf(
Block(
id = root,
@ -68,7 +69,8 @@ object MockBlockFactory {
marks = emptyList(),
style = style
),
children = emptyList()
children = emptyList(),
backgroundColor = backgroundColor
)
)

View file

@ -1,13 +1,12 @@
package com.anytypeio.anytype.presentation.editor
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.TextStyle
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingType
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.markup.MarkupStyleDescriptor
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.MockDataFactory
@ -84,26 +83,11 @@ class ControlPanelStateReducerTest {
fun `state should hide mentions when cursor before mentions start and widget is visible`() {
val given = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = true,
cursorCoordinate = 333,
mentionFilter = "start",
mentionFrom = 10
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -141,26 +125,11 @@ class ControlPanelStateReducerTest {
fun `state should not hide mentions when cursor after mention start`() {
val given = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = true
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = true,
cursorCoordinate = 333,
mentionFilter = "start",
mentionFrom = 10
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -195,26 +164,11 @@ class ControlPanelStateReducerTest {
fun `state should hide mentions after focus changed`() {
val given = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = true
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = true,
cursorCoordinate = 333,
mentionFilter = "start",
mentionFrom = 10
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -236,7 +190,8 @@ class ControlPanelStateReducerTest {
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
)
),
mainToolbar = ControlPanelState.Toolbar.Main(isVisible = true)
)
assertEquals(
@ -251,26 +206,11 @@ class ControlPanelStateReducerTest {
val id = MockDataFactory.randomUuid()
val given = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false,
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = true,
cursorCoordinate = 333,
mentionFilter = "start",
mentionFrom = 10
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -317,26 +257,8 @@ class ControlPanelStateReducerTest {
fun `state should have only focus changed`() {
val given = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = true
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -364,26 +286,14 @@ class ControlPanelStateReducerTest {
fun `should return to initial state when focus is cleared`() {
val given = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = true
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -410,15 +320,6 @@ class ControlPanelStateReducerTest {
val id = MockDataFactory.randomUuid()
val nonSelected = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false,
count = 2
@ -428,34 +329,13 @@ class ControlPanelStateReducerTest {
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
val selected = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
count = 0
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -478,52 +358,16 @@ class ControlPanelStateReducerTest {
val id = MockDataFactory.randomUuid()
val selectedZero = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
count = 0
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
val expected = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
count = 3
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -541,123 +385,124 @@ class ControlPanelStateReducerTest {
}
@Test
fun `should update style toolbar state with italic true after selection changed`() = runBlocking {
fun `should update style toolbar state with italic true after selection changed`() =
runBlocking {
val block = Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
content = Block.Content.Text(
text = "Foo Bar",
style = Block.Content.Text.Style.P,
marks = listOf(
Block.Content.Text.Mark(
val block = Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
content = Block.Content.Text(
text = "Foo Bar",
style = Block.Content.Text.Style.P,
marks = listOf(
Block.Content.Text.Mark(
range = IntRange(0, 2),
type = Block.Content.Text.Mark.Type.BOLD
),
Block.Content.Text.Mark(
range = IntRange(4, 6),
type = Block.Content.Text.Mark.Type.ITALIC
)
)
),
fields = Block.Fields.empty()
)
val selectionFirst = IntRange(0, 2)
val selectionSecond = IntRange(4, 6)
val given = ControlPanelState(
styleTextToolbar = ControlPanelState.Toolbar.Styling.reset(),
navigationToolbar = ControlPanelState.Toolbar.Navigation.reset(),
mainToolbar = ControlPanelState.Toolbar.Main.reset(),
multiSelect = ControlPanelState.Toolbar.MultiSelect.reset(),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar.reset(),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset(),
markupMainToolbar = ControlPanelState.Toolbar.MarkupMainToolbar(
isVisible = true,
style = MarkupStyleDescriptor.Default(
isBold = true,
isItalic = false,
isCode = false,
isLinked = false,
isStrikethrough = false,
markupTextColor = "yellow",
markupUrl = null,
markupHighlightColor = "red",
range = IntRange(0, 2),
type = Block.Content.Text.Mark.Type.BOLD
blockBackroundColor = null,
blockTextColor = null
),
Block.Content.Text.Mark(
range = IntRange(4, 6),
type = Block.Content.Text.Mark.Type.ITALIC
supportedTypes = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
)
),
fields = Block.Fields.empty()
)
)
val selectionFirst = IntRange(0, 2)
val selectionSecond = IntRange(4, 6)
val result = runBlocking {
reducer.reduce(
state = given,
event = ControlPanelMachine.Event.OnSelectionChanged(
selection = selectionSecond,
target = block
)
)
}
val given = ControlPanelState(
styleTextToolbar = ControlPanelState.Toolbar.Styling.reset(),
navigationToolbar = ControlPanelState.Toolbar.Navigation.reset(),
mainToolbar = ControlPanelState.Toolbar.Main.reset(),
multiSelect = ControlPanelState.Toolbar.MultiSelect.reset(),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar.reset(),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset(),
markupMainToolbar = ControlPanelState.Toolbar.MarkupMainToolbar(
isVisible = true,
style = MarkupStyleDescriptor.Default(
isBold = true,
isItalic = false,
isCode = false,
isLinked = false,
isStrikethrough = false,
markupTextColor = "yellow",
markupUrl = null,
markupHighlightColor = "red",
range = IntRange(0, 2),
blockBackroundColor = null,
blockTextColor = null
val expected = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
supportedTypes = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling.reset(),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
),
markupMainToolbar = ControlPanelState.Toolbar.MarkupMainToolbar(
isVisible = true,
style = MarkupStyleDescriptor.Default(
isBold = false,
isItalic = true,
isCode = false,
isLinked = false,
isStrikethrough = false,
markupTextColor = null,
markupUrl = null,
markupHighlightColor = null,
range = IntRange(4, 6),
blockBackroundColor = null,
blockTextColor = null
),
supportedTypes = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
)
)
)
val result = runBlocking {
reducer.reduce(
state = given,
event = ControlPanelMachine.Event.OnSelectionChanged(
selection = selectionSecond,
target = block
)
assertEquals(
expected = expected,
actual = result
)
}
val expected = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling.reset(),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
),
markupMainToolbar = ControlPanelState.Toolbar.MarkupMainToolbar(
isVisible = true,
style = MarkupStyleDescriptor.Default(
isBold = false,
isItalic = true,
isCode = false,
isLinked = false,
isStrikethrough = false,
markupTextColor = null,
markupUrl = null,
markupHighlightColor = null,
range = IntRange(4, 6),
blockBackroundColor = null,
blockTextColor = null
),
supportedTypes = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
)
)
assertEquals(
expected = expected,
actual = result
)
}
@Test
fun `should show style toolbar in markup mode after main toolbar style clicked`() {
@ -693,19 +538,8 @@ class ControlPanelStateReducerTest {
}
val given = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = true
),
styleTextToolbar = ControlPanelState.Toolbar.Styling.reset(),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar.reset(),
slashWidget = ControlPanelState.Toolbar.SlashWidget(
isVisible = false
)
)
@ -722,12 +556,8 @@ class ControlPanelStateReducerTest {
val result = runBlocking {
reducer.reduce(
state = given,
event = ControlPanelMachine.Event.OnBlockActionToolbarStyleClicked(
target = block,
selection = IntRange(0, 3),
focused = true,
urlBuilder = urlBuilder,
details = Block.Details(mapOf())
event = ControlPanelMachine.Event.StylingToolbar.OnUpdateTextToolbar(
state = StyleToolbarState.Text(textStyle = Block.Content.Text.Style.P)
)
)
}
@ -740,40 +570,8 @@ class ControlPanelStateReducerTest {
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = true,
target = ControlPanelState.Toolbar.Styling.Target(
id = id,
text = "Foo Bar",
color = "yellow",
background = "red",
alignment = Alignment.START,
marks = listOf(Markup.Mark.Bold(0, 3))
),
config = StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledAlignment = listOf(),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
),
props = ControlPanelState.Toolbar.Styling.Props(
isBold = true,
isItalic = false,
isStrikethrough = false,
isCode = false,
isLinked = false,
alignment = Alignment.START,
color = "yellow",
background = "red"
)
state = StyleToolbarState.Text(Block.Content.Text.Style.P),
isVisible = true
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
@ -797,7 +595,7 @@ class ControlPanelStateReducerTest {
children = emptyList(),
content = Block.Content.Text(
text = "Foo Bar",
style = Block.Content.Text.Style.P,
style = Block.Content.Text.Style.BULLET,
marks = listOf(
Block.Content.Text.Mark(
range = 0..3,
@ -816,7 +614,7 @@ class ControlPanelStateReducerTest {
state = ControlPanelState.init(),
event = ControlPanelMachine.Event.OnFocusChanged(
id = id,
style = Block.Content.Text.Style.P
style = Block.Content.Text.Style.BULLET
)
)
}
@ -832,87 +630,25 @@ class ControlPanelStateReducerTest {
}
val given = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = true
),
styleTextToolbar = ControlPanelState.Toolbar.Styling.reset(),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar.reset(),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
)
)
val result = runBlocking {
reducer.reduce(
state = given,
event = ControlPanelMachine.Event.OnBlockActionToolbarStyleClicked(
target = block,
selection = IntRange(1, 1),
focused = true,
urlBuilder = urlBuilder,
details = Block.Details(mapOf())
event = ControlPanelMachine.Event.StylingToolbar.OnUpdateTextToolbar(
state = StyleToolbarState.Text(textStyle = Block.Content.Text.Style.BULLET)
)
)
}
val expected = ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = true,
target = ControlPanelState.Toolbar.Styling.Target(
id = id,
text = "Foo Bar",
color = "yellow",
background = "red",
alignment = Alignment.CENTER,
marks = listOf(
Markup.Mark.Bold(0, 3)
)
),
config = StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledAlignment = listOf(
Alignment.START,
Alignment.CENTER,
Alignment.END
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
)
),
props = ControlPanelState.Toolbar.Styling.Props(
isBold = false,
isItalic = false,
isStrikethrough = false,
isCode = false,
isLinked = false,
alignment = Alignment.CENTER,
color = "yellow",
background = "red"
)
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar.reset(),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
state = StyleToolbarState.Text(textStyle = TextStyle.BULLET),
isVisible = true
)
)
assertEquals(
expected = expected,
@ -987,12 +723,6 @@ class ControlPanelStateReducerTest {
)
val expected = ControlPanelState(
styleTextToolbar = ControlPanelState.Toolbar.Styling.reset(),
navigationToolbar = ControlPanelState.Toolbar.Navigation.reset(),
mainToolbar = ControlPanelState.Toolbar.Main.reset(),
multiSelect = ControlPanelState.Toolbar.MultiSelect.reset(),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar.reset(),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset(),
markupMainToolbar = ControlPanelState.Toolbar.MarkupMainToolbar(
isVisible = true,
style = MarkupStyleDescriptor.Default(

View file

@ -77,7 +77,6 @@ import com.anytypeio.anytype.presentation.editor.editor.Interactor
import com.anytypeio.anytype.presentation.editor.editor.InternalDetailModificationManager
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.Orchestrator
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.ViewState
import com.anytypeio.anytype.presentation.editor.editor.actions.ActionItemType
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
@ -85,7 +84,7 @@ import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.UiBlock
import com.anytypeio.anytype.presentation.editor.editor.pattern.DefaultPatternMatcher
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
@ -4531,12 +4530,7 @@ open class EditorViewModelTest {
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = true,
style = Block.Content.Text.Style.P,
config = StyleConfig.emptyState(),
props = ControlPanelState.Toolbar.Styling.Props(
color = ThemeColor.DEFAULT.title,
background = ThemeColor.DEFAULT.title
)
state = StyleToolbarState.Text(Block.Content.Text.Style.P)
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,

View file

@ -18,7 +18,7 @@ import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelStat
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.UiBlock
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarState
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.presentation.util.TXT
import com.anytypeio.anytype.test_utils.MockDataFactory
@ -149,27 +149,11 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
vm.controlPanelViewState.test().apply {
assertValue(
ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
isScrollAndMoveEnabled = false,
count = 0
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
)
)
)
}
@ -249,27 +233,11 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
vm.controlPanelViewState.test().apply {
assertValue(
ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
isScrollAndMoveEnabled = false,
count = 0
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
)
)
)
}
@ -365,27 +333,11 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
vm.controlPanelViewState.test().apply {
assertValue(
ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
isScrollAndMoveEnabled = false,
count = 0
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
)
)
)
}
@ -1336,12 +1288,7 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
val expectedState = ControlPanelState(
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = true,
style = null,
config = StyleConfig.emptyState(),
props = ControlPanelState.Toolbar.Styling.Props(
color = ThemeColor.DEFAULT.title,
background = ThemeColor.DEFAULT.title
)
state = StyleToolbarState.Text(null)
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
@ -1429,7 +1376,7 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
),
styleBackgroundToolbar = ControlPanelState.Toolbar.Styling.Background(
isVisible = true,
selectedBackground = null
state = StyleToolbarState.Background(background = null)
)
)
@ -1510,7 +1457,7 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
),
styleBackgroundToolbar = ControlPanelState.Toolbar.Styling.Background(
isVisible = true,
selectedBackground = backgroundA
state = StyleToolbarState.Background(background = backgroundA)
)
)
@ -1593,7 +1540,7 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
),
styleBackgroundToolbar = ControlPanelState.Toolbar.Styling.Background(
isVisible = true,
selectedBackground = backgroundA
state = StyleToolbarState.Background(background = backgroundA)
)
)
@ -1674,7 +1621,7 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
),
styleBackgroundToolbar = ControlPanelState.Toolbar.Styling.Background(
isVisible = true,
selectedBackground = ThemeColor.DEFAULT.title
state = StyleToolbarState.Background(background = ThemeColor.DEFAULT.title)
)
)

View file

@ -124,27 +124,11 @@ class EditorScrollAndMoveTest : EditorPresentationTestSetup() {
vm.controlPanelViewState.test().apply {
assertValue(
ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
isScrollAndMoveEnabled = true,
count = 1
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
)
)
)
}
@ -218,27 +202,11 @@ class EditorScrollAndMoveTest : EditorPresentationTestSetup() {
vm.controlPanelViewState.test().apply {
assertValue(
ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = false
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = true,
isScrollAndMoveEnabled = false,
count = 1
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
)
)
)
}
@ -294,26 +262,7 @@ class EditorScrollAndMoveTest : EditorPresentationTestSetup() {
ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = true
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false,
isScrollAndMoveEnabled = false,
isQuickScrollAndMoveMode = false,
count = 0
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
)
)
)
}
@ -412,25 +361,7 @@ class EditorScrollAndMoveTest : EditorPresentationTestSetup() {
ControlPanelState(
navigationToolbar = ControlPanelState.Toolbar.Navigation(
isVisible = true
),
mainToolbar = ControlPanelState.Toolbar.Main(
isVisible = false
),
styleTextToolbar = ControlPanelState.Toolbar.Styling(
isVisible = false
),
multiSelect = ControlPanelState.Toolbar.MultiSelect(
isVisible = false,
isScrollAndMoveEnabled = false,
count = 0
),
mentionToolbar = ControlPanelState.Toolbar.MentionToolbar(
isVisible = false,
cursorCoordinate = null,
mentionFilter = null,
mentionFrom = null
),
slashWidget = ControlPanelState.Toolbar.SlashWidget.reset()
)
)
)
}

View file

@ -0,0 +1,116 @@
package com.anytypeio.anytype.presentation.editor.editor.styling
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.TextStyle
import com.anytypeio.anytype.test_utils.MockDataFactory
import org.junit.Assert
import org.junit.Test
class StyleToolbarExtKtTest {
@Test
fun `should return style text state with nullable selected style`() {
val child = MockDataFactory.randomUuid()
val given1 = Block(
id = child,
fields = Block.Fields(emptyMap()),
content = Block.Content.Text(
style = Block.Content.Text.Style.P,
text = MockDataFactory.randomString(),
marks = listOf()
),
backgroundColor = null,
children = emptyList()
)
val given2 = Block(
id = child,
fields = Block.Fields(emptyMap()),
content = Block.Content.Text(
style = Block.Content.Text.Style.NUMBERED,
text = MockDataFactory.randomString(),
marks = listOf()
),
backgroundColor = null,
children = emptyList()
)
val given3 = Block(
id = child,
fields = Block.Fields(emptyMap()),
content = Block.Content.Text(
style = Block.Content.Text.Style.P,
text = MockDataFactory.randomString(),
marks = listOf()
),
backgroundColor = null,
children = emptyList()
)
val result =
listOf(given1, given2, given3)
.map { it.content.asText() }
.getStyleTextToolbarState()
val expected = StyleToolbarState.Text(
textStyle = null
)
Assert.assertEquals(expected, result)
}
@Test
fun `should return style text state with bullet selected style`() {
val child = MockDataFactory.randomUuid()
val given1 = Block(
id = child,
fields = Block.Fields(emptyMap()),
content = Block.Content.Text(
style = Block.Content.Text.Style.BULLET,
text = MockDataFactory.randomString(),
marks = listOf()
),
backgroundColor = null,
children = emptyList()
)
val given2 = Block(
id = child,
fields = Block.Fields(emptyMap()),
content = Block.Content.Text(
style = Block.Content.Text.Style.BULLET,
text = MockDataFactory.randomString(),
marks = listOf()
),
backgroundColor = null,
children = emptyList()
)
val given3 = Block(
id = child,
fields = Block.Fields(emptyMap()),
content = Block.Content.Text(
style = Block.Content.Text.Style.BULLET,
text = MockDataFactory.randomString(),
marks = listOf()
),
backgroundColor = null,
children = emptyList()
)
val result =
listOf(given1, given2, given3)
.map { it.content.asText() }
.getStyleTextToolbarState()
val expected = StyleToolbarState.Text(
textStyle = TextStyle.BULLET
)
Assert.assertEquals(expected, result)
}
}

View file

@ -3,10 +3,6 @@ package com.anytypeio.anytype.sample
import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.styling.StyleConfig
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingType
import kotlinx.android.synthetic.main.activity_style.*
class StyleActivity: AppCompatActivity() {
@ -20,38 +16,6 @@ class StyleActivity: AppCompatActivity() {
}
button.setOnClickListener {
styleToolbar.update(
config = StyleConfig(
visibleTypes = listOf(
StylingType.STYLE,
StylingType.TEXT_COLOR,
StylingType.BACKGROUND
),
enabledMarkup = listOf(
Markup.Type.BOLD,
Markup.Type.ITALIC,
Markup.Type.STRIKETHROUGH,
Markup.Type.KEYBOARD,
Markup.Type.LINK
),
enabledAlignment = listOf(
Alignment.START,
Alignment.END
)
),
props = com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState.Toolbar.Styling.Props(
isBold = false,
isItalic = false,
isStrikethrough = true,
isCode = false,
isLinked = false,
color = null,
background = null,
alignment = null
)
)
//styleToolbar.showWithAnimation()
}
}
}