From 0d8e4dee358389c1020c0e74d6b271b06b29bd45 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 30 Jul 2022 14:00:08 +0300 Subject: [PATCH] Update reader mode selection ui --- .../base/ui/widgets/CheckableButtonGroup.kt | 86 ++++++++++++++++++- .../kotatsu/base/ui/widgets/CornerData.kt | 47 ++++++++++ .../ui/config/ReaderConfigBottomSheet.kt | 13 +-- .../main/res/layout/sheet_reader_config.xml | 5 +- app/src/main/res/values/styles.xml | 7 +- 5 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/CornerData.kt diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/CheckableButtonGroup.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/CheckableButtonGroup.kt index 77d4acc2c..09931d540 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/CheckableButtonGroup.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/CheckableButtonGroup.kt @@ -8,23 +8,34 @@ import android.widget.LinearLayout import androidx.annotation.AttrRes import androidx.annotation.IdRes import androidx.core.view.children +import com.google.android.material.R as materialR import com.google.android.material.button.MaterialButton +import com.google.android.material.shape.ShapeAppearanceModel +@Deprecated("") class CheckableButtonGroup @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, - @AttrRes defStyleAttr: Int = 0, -) : LinearLayout(context, attrs, defStyleAttr), View.OnClickListener { + @AttrRes defStyleAttr: Int = materialR.attr.materialButtonToggleGroupStyle, +) : LinearLayout(context, attrs, defStyleAttr, materialR.style.Widget_MaterialComponents_MaterialButtonToggleGroup), + View.OnClickListener { + + private val originalCornerData = ArrayList() var onCheckedChangeListener: OnCheckedChangeListener? = null override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) { if (child is MaterialButton) { - child.setOnClickListener(this) + setupButton(child) } super.addView(child, index, params) } + override fun onFinishInflate() { + super.onFinishInflate() + updateChildShapes() + } + override fun onClick(v: View) { setCheckedId(v.id) } @@ -36,7 +47,74 @@ class CheckableButtonGroup @JvmOverloads constructor( onCheckedChangeListener?.onCheckedChanged(this, viewRes) } + private fun updateChildShapes() { + val childCount = childCount + val firstVisibleChildIndex = 0 + val lastVisibleChildIndex = childCount - 1 + for (i in 0 until childCount) { + val button: MaterialButton = getChildAt(i) as? MaterialButton ?: continue + if (button.visibility == GONE) { + continue + } + val builder = button.shapeAppearanceModel.toBuilder() + val newCornerData: CornerData? = + getNewCornerData(i, firstVisibleChildIndex, lastVisibleChildIndex) + updateBuilderWithCornerData(builder, newCornerData) + button.shapeAppearanceModel = builder.build() + } + } + + private fun setupButton(button: MaterialButton) { + button.setOnClickListener(this) + button.isElegantTextHeight = false + // Saves original corner data + val shapeAppearanceModel: ShapeAppearanceModel = button.shapeAppearanceModel + originalCornerData.add( + CornerData( + shapeAppearanceModel.topLeftCornerSize, + shapeAppearanceModel.bottomLeftCornerSize, + shapeAppearanceModel.topRightCornerSize, + shapeAppearanceModel.bottomRightCornerSize, + ), + ) + } + + private fun getNewCornerData( + index: Int, + firstVisibleChildIndex: Int, + lastVisibleChildIndex: Int, + ): CornerData? { + val cornerData: CornerData = originalCornerData.get(index) + + // If only one (visible) child exists, use its original corners + if (firstVisibleChildIndex == lastVisibleChildIndex) { + return cornerData + } + val isHorizontal = orientation == HORIZONTAL + if (index == firstVisibleChildIndex) { + return if (isHorizontal) cornerData.start(this) else cornerData.top() + } + return if (index == lastVisibleChildIndex) { + if (isHorizontal) cornerData.end(this) else cornerData.bottom() + } else null + } + + private fun updateBuilderWithCornerData( + shapeAppearanceModelBuilder: ShapeAppearanceModel.Builder, + cornerData: CornerData?, + ) { + if (cornerData == null) { + shapeAppearanceModelBuilder.setAllCornerSizes(0f) + return + } + shapeAppearanceModelBuilder + .setTopLeftCornerSize(cornerData.topLeft) + .setBottomLeftCornerSize(cornerData.bottomLeft) + .setTopRightCornerSize(cornerData.topRight) + .setBottomRightCornerSize(cornerData.bottomRight) + } + fun interface OnCheckedChangeListener { fun onCheckedChanged(group: CheckableButtonGroup, checkedId: Int) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/CornerData.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/CornerData.kt new file mode 100644 index 000000000..9818d1f27 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/CornerData.kt @@ -0,0 +1,47 @@ +package org.koitharu.kotatsu.base.ui.widgets + +import android.view.View +import androidx.core.view.ViewCompat +import com.google.android.material.shape.AbsoluteCornerSize +import com.google.android.material.shape.CornerSize + +class CornerData( + var topLeft: CornerSize, + var bottomLeft: CornerSize, + var topRight: CornerSize, + var bottomRight: CornerSize, +) { + + fun start(view: View): CornerData { + return if (isLayoutRtl(view)) right() else left() + } + + fun end(view: View): CornerData { + return if (isLayoutRtl(view)) left() else right() + } + + fun left(): CornerData { + return CornerData(topLeft, bottomLeft, noCorner, noCorner) + } + + fun right(): CornerData { + return CornerData(noCorner, noCorner, topRight, bottomRight) + } + + fun top(): CornerData { + return CornerData(topLeft, noCorner, topRight, noCorner) + } + + fun bottom(): CornerData { + return CornerData(noCorner, bottomLeft, noCorner, bottomRight) + } + + private companion object { + + val noCorner: CornerSize = AbsoluteCornerSize(0f) + + fun isLayoutRtl(view: View): Boolean { + return ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL + } + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt index 02767a590..e89430bc6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt @@ -10,11 +10,11 @@ import androidx.core.view.isGone import androidx.fragment.app.FragmentManager import androidx.fragment.app.activityViewModels import androidx.lifecycle.flowWithLifecycle +import com.google.android.material.button.MaterialButtonToggleGroup import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet -import org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.databinding.SheetReaderConfigBinding import org.koitharu.kotatsu.reader.ui.PageSaveContract @@ -26,9 +26,9 @@ import org.koitharu.kotatsu.utils.ext.withArgs class ReaderConfigBottomSheet : BaseBottomSheet(), - CheckableButtonGroup.OnCheckedChangeListener, ActivityResultCallback, - View.OnClickListener { + View.OnClickListener, + MaterialButtonToggleGroup.OnButtonCheckedListener { private val viewModel by activityViewModels() private val savePageRequest = registerForActivityResult(PageSaveContract(), this) @@ -53,7 +53,7 @@ class ReaderConfigBottomSheet : binding.buttonReversed.isChecked = mode == ReaderMode.REVERSED binding.buttonWebtoon.isChecked = mode == ReaderMode.WEBTOON - binding.checkableGroup.onCheckedChangeListener = this + binding.checkableGroup.addOnButtonCheckedListener(this) binding.buttonSavePage.setOnClickListener(this) binding.buttonScreenRotate.setOnClickListener(this) binding.buttonSettings.setOnClickListener(this) @@ -75,7 +75,10 @@ class ReaderConfigBottomSheet : } } - override fun onCheckedChanged(group: CheckableButtonGroup, checkedId: Int) { + override fun onButtonChecked(group: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean) { + if (!isChecked) { + return + } val newMode = when (checkedId) { R.id.button_standard -> ReaderMode.STANDARD R.id.button_webtoon -> ReaderMode.WEBTOON diff --git a/app/src/main/res/layout/sheet_reader_config.xml b/app/src/main/res/layout/sheet_reader_config.xml index a0cc624ef..5a173a268 100644 --- a/app/src/main/res/layout/sheet_reader_config.xml +++ b/app/src/main/res/layout/sheet_reader_config.xml @@ -55,7 +55,7 @@ android:text="@string/read_mode" android:textAppearance="?textAppearanceTitleSmall" /> - @@ -92,7 +91,7 @@ android:text="@string/webtoon" app:icon="@drawable/ic_script" /> - + center - 4dp + 2dp false 2 2 top - 10dp + 12dp 10dp + 6dp + 6dp + false ?shapeAppearanceCornerMedium