Using a seekbar to select grid size

This is mostly the same amount of options for phones, but opens more for tablets

There's also actual math now for setting the grid size and a float used (rounded to the nearest .25)

I could just add back portrait/landscape per row....I could but

This seeker also has a bubble tooltip to show the current value, might use elsewhere later
This commit is contained in:
Jays2Kings 2021-04-16 22:59:55 -04:00
parent 4cbf1cc785
commit 0cf56ca335
13 changed files with 281 additions and 98 deletions

View File

@ -110,6 +110,25 @@ object Migrations {
remove("enable_doh") remove("enable_doh")
} }
} }
val oldGridSize = prefs.getInt("grid_size", -1)
// Migrate to float for grid size
if (oldGridSize != -1) {
prefs.edit {
putFloat(
PreferenceKeys.gridSize,
when (oldGridSize) {
4 -> 3f
3 -> 1.5f
2 -> 1f
1 -> 0f
0 -> -.5f
else -> .5f
}
)
remove("grid_size")
}
}
} }
return true return true
} }

View File

@ -150,7 +150,7 @@ object PreferenceKeys {
const val libraryLayout = "pref_display_library_layout" const val libraryLayout = "pref_display_library_layout"
const val gridSize = "grid_size" const val gridSize = "grid_size_float"
const val uniformGrid = "uniform_grid" const val uniformGrid = "uniform_grid"

View File

@ -242,7 +242,7 @@ class PreferencesHelper(val context: Context) {
fun libraryLayout() = flowPrefs.getInt(Keys.libraryLayout, 2) fun libraryLayout() = flowPrefs.getInt(Keys.libraryLayout, 2)
fun gridSize() = flowPrefs.getInt(Keys.gridSize, 2) fun gridSize() = flowPrefs.getFloat(Keys.gridSize, 1f)
fun uniformGrid() = flowPrefs.getBoolean(Keys.uniformGrid, true) fun uniformGrid() = flowPrefs.getBoolean(Keys.uniformGrid, true)

View File

@ -794,13 +794,7 @@ class LibraryController(
end = 0 end = 0
) )
} else { } else {
binding.libraryGridRecycler.recycler.columnWidth = when (preferences.gridSize().get()) { binding.libraryGridRecycler.recycler.setGridSize(preferences)
1 -> 1f
2 -> 1.25f
3 -> 1.66f
4 -> 3f
else -> .75f
}
binding.libraryGridRecycler.recycler.updatePaddingRelative( binding.libraryGridRecycler.recycler.updatePaddingRelative(
start = 5.dpToPx, start = 5.dpToPx,
end = 5.dpToPx end = 5.dpToPx
@ -809,7 +803,12 @@ class LibraryController(
} }
private fun setPreferenceFlows() { private fun setPreferenceFlows() {
listOf(preferences.libraryLayout(), preferences.uniformGrid(), preferences.gridSize(), preferences.unreadBadgeType()).forEach { listOf(
preferences.libraryLayout(),
preferences.uniformGrid(),
preferences.gridSize(),
preferences.unreadBadgeType()
).forEach {
it.asFlow() it.asFlow()
.drop(1) .drop(1)
.onEach { .onEach {

View File

@ -1,10 +1,24 @@
package eu.kanade.tachiyomi.ui.library.display package eu.kanade.tachiyomi.ui.library.display
import android.animation.ValueAnimator
import android.content.Context import android.content.Context
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.util.AttributeSet import android.util.AttributeSet
import android.view.ViewTreeObserver
import android.widget.SeekBar
import androidx.core.animation.addListener
import androidx.core.view.isVisible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.LibraryDisplayLayoutBinding import eu.kanade.tachiyomi.databinding.LibraryDisplayLayoutBinding
import eu.kanade.tachiyomi.util.bindToPreference import eu.kanade.tachiyomi.util.bindToPreference
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.rowsForValue
import eu.kanade.tachiyomi.widget.BaseLibraryDisplayView import eu.kanade.tachiyomi.widget.BaseLibraryDisplayView
import eu.kanade.tachiyomi.widget.EndAnimatorListener
import kotlin.math.roundToInt
class LibraryDisplayView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class LibraryDisplayView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
BaseLibraryDisplayView<LibraryDisplayLayoutBinding>(context, attrs) { BaseLibraryDisplayView<LibraryDisplayLayoutBinding>(context, attrs) {
@ -13,6 +27,93 @@ class LibraryDisplayView @JvmOverloads constructor(context: Context, attrs: Attr
override fun initGeneralPreferences() { override fun initGeneralPreferences() {
binding.displayGroup.bindToPreference(preferences.libraryLayout()) binding.displayGroup.bindToPreference(preferences.libraryLayout())
binding.uniformGrid.bindToPreference(preferences.uniformGrid()) binding.uniformGrid.bindToPreference(preferences.uniformGrid())
binding.gridSizeToggleGroup.bindToPreference(preferences.gridSize()) binding.gridSeekbar.progress = ((preferences.gridSize().get() + .5f) * 2f).roundToInt()
binding.resetGridSize.setOnClickListener {
binding.gridSeekbar.progress = 3
}
binding.root.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (binding.root.width > 0) {
setGridText(binding.gridSeekbar.progress)
binding.root.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
})
binding.gridSeekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (seekBar != null && fromUser) {
alpha = 1f
isVisible = true
adjustSeekBarTip(seekBar, progress)
}
if (!fromUser) {
preferences.gridSize().set((progress / 2f) - .5f)
}
setGridText(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
with(binding.seekBarTextView.root) {
alpha = 0f
isVisible = true
animate().alpha(1f).setDuration(250L).start()
seekBar?.post {
adjustSeekBarTip(seekBar, seekBar.progress)
}
}
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
preferences.gridSize().set(((seekBar?.progress ?: 2) / 2f) - .5f)
with(binding.seekBarTextView.root) {
isVisible = true
alpha = 1f
post {
val anim =
ValueAnimator.ofFloat(
1f,
0f
) // animate().alpha(0f).setDuration(250L)
anim.duration = 250
anim.startDelay = 500
anim.addUpdateListener {
alpha = it.animatedValue as Float
}
anim.addListener {
EndAnimatorListener {
isVisible = false
}
}
anim.start()
}
}
}
})
}
private fun setGridText(progress: Int) {
with(binding.gridSizeText) {
val rows = this@LibraryDisplayView.rowsForValue(progress)
val titleText = context.getString(R.string.grid_size)
val subtitleText = context.getString(R.string._per_row, rows)
val spannable = SpannableStringBuilder(titleText + "\n" + subtitleText)
spannable.setSpan(
ForegroundColorSpan(context.getResourceColor(android.R.attr.textColorSecondary)),
titleText.length + 1,
spannable.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
text = spannable
}
}
private fun adjustSeekBarTip(seekBar: SeekBar, progress: Int) {
with(binding.seekBarTextView.root) {
val value =
(progress * (seekBar.width - 12.dpToPx - 2 * seekBar.thumbOffset)) / seekBar.max
text = this@LibraryDisplayView.rowsForValue(progress).toString()
x = seekBar.x + value + seekBar.thumbOffset / 2 + 5.dpToPx
y = seekBar.y + binding.gridSizeLayout.y - 6.dpToPx - height
}
} }
} }

View File

@ -11,7 +11,6 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.setting.SettingsLibraryController import eu.kanade.tachiyomi.ui.setting.SettingsLibraryController
import eu.kanade.tachiyomi.util.view.compatToolTipText import eu.kanade.tachiyomi.util.view.compatToolTipText
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.withFadeTransaction import eu.kanade.tachiyomi.util.view.withFadeTransaction
import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt

View File

@ -167,13 +167,7 @@ open class BrowseSourceController(bundle: Bundle) :
} }
} else { } else {
(binding.catalogueView.inflate(R.layout.manga_recycler_autofit) as AutofitRecyclerView).apply { (binding.catalogueView.inflate(R.layout.manga_recycler_autofit) as AutofitRecyclerView).apply {
columnWidth = when (preferences.gridSize().get()) { setGridSize(preferences)
1 -> 1f
2 -> 1.25f
3 -> 1.66f
4 -> 3f
else -> .75f
}
(layoutManager as androidx.recyclerview.widget.GridLayoutManager).spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() { (layoutManager as androidx.recyclerview.widget.GridLayoutManager).spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int { override fun getSpanSize(position: Int): Int {

View File

@ -37,8 +37,13 @@ import eu.kanade.tachiyomi.util.system.ThemeUtil
import eu.kanade.tachiyomi.util.system.contextCompatColor import eu.kanade.tachiyomi.util.system.contextCompatColor
import eu.kanade.tachiyomi.util.system.getPrefTheme import eu.kanade.tachiyomi.util.system.getPrefTheme
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.pxToDp
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import kotlin.math.max
import kotlin.math.pow
import kotlin.math.roundToInt
/** /**
* Returns coordinates of view. * Returns coordinates of view.
@ -338,6 +343,17 @@ fun RecyclerView.smoothScrollToTop() {
} }
} }
fun View.rowsForValue(value: Int): Int {
return rowsForValue((value / 2f) - .5f)
}
fun View.rowsForValue(value: Float): Int {
val size = 1.5f.pow(value)
val trueSize = AutofitRecyclerView.MULTIPLE * ((size * 100 / AutofitRecyclerView.MULTIPLE).roundToInt()) / 100f
val dpWidth = (measuredWidth.pxToDp / 100f).roundToInt()
return max(1, (dpWidth / trueSize).roundToInt())
}
var View.compatToolTipText: CharSequence? var View.compatToolTipText: CharSequence?
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
tooltipText tooltipText

View File

@ -3,8 +3,10 @@ package eu.kanade.tachiyomi.widget
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.system.pxToDp import eu.kanade.tachiyomi.util.system.pxToDp
import kotlin.math.max import kotlin.math.max
import kotlin.math.pow
import kotlin.math.roundToInt import kotlin.math.roundToInt
class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
@ -51,6 +53,12 @@ class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: Att
setSpan() setSpan()
} }
fun setGridSize(preferences: PreferencesHelper) {
val size = 1.5f.pow(preferences.gridSize().get())
val trueSize = MULTIPLE * ((size * 100 / MULTIPLE).roundToInt()) / 100f
columnWidth = trueSize
}
private fun setSpan(force: Boolean = false) { private fun setSpan(force: Boolean = false) {
if ((spanCount == 0 || force) && columnWidth > 0) { if ((spanCount == 0 || force) && columnWidth > 0) {
val dpWidth = (measuredWidth.pxToDp / 100f).roundToInt() val dpWidth = (measuredWidth.pxToDp / 100f).roundToInt()
@ -58,4 +66,9 @@ class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: Att
spanCount = count spanCount = count
} }
} }
companion object {
private const val MULTIPLE_PERCENT = 0.25f
const val MULTIPLE = MULTIPLE_PERCENT * 100
}
} }

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,3.6c-3.9,0 -7,3.1 -7,7c0,5.2 7,9.8 7,9.8s7,-4.5 7,-9.8C19,6.7 15.9,3.6 12,3.6z"/>
</vector>

View File

@ -1,93 +1,111 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.ui.library.display.LibraryDisplayView <eu.kanade.tachiyomi.ui.library.display.LibraryDisplayView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <FrameLayout
android:id="@+id/linear_layout" android:id="@+id/display_frame_layout"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content">
<RadioGroup <LinearLayout
android:id="@+id/display_group" android:id="@+id/linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingStart="12dp"
android:paddingEnd="12dp">
<com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/list" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/compact_grid" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/comfortable_grid" />
</RadioGroup>
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent">
android:layout_marginTop="16dp"
android:paddingStart="16dp"
android:paddingEnd="12dp"
android:text="@string/grid_options" />
<RadioGroup <RadioGroup
android:id="@+id/grid_size_toggle_group" android:id="@+id/display_group"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:orientation="horizontal">
<com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/x_small" /> android:orientation="vertical"
android:paddingStart="12dp"
android:paddingEnd="12dp">
<com.google.android.material.radiobutton.MaterialRadioButton <com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/list" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/compact_grid" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/comfortable_grid" />
</RadioGroup>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/grid_size_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/grid_size_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/grid_seekbar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="0.0"
android:textAppearance="@style/TextAppearance.Regular.Body1"
android:textSize="15sp"
android:text="@string/grid_size"
android:layout_weight="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<SeekBar
android:id="@+id/grid_seekbar"
android:layout_marginStart="12dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:max="7"
android:layout_width="0dp"
app:layout_constraintStart_toEndOf="@id/grid_size_text"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/reset_grid_size"
app:layout_constraintWidth_max="350dp"
android:layout_height="wrap_content"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/reset_grid_size"
android:layout_marginStart="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/grid_seekbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
style="@style/Theme.Widget.Button.TextButton"
android:text="@string/reset" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/uniform_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="@string/uniform_grid_covers" />
</LinearLayout>
android:text="@string/small" /> <include
android:id="@+id/seek_bar_text_view"
<com.google.android.material.radiobutton.MaterialRadioButton android:visibility="gone"
android:layout_width="wrap_content" android:alpha="0"
android:layout_height="wrap_content" tools:alpha="1"
android:layout_marginStart="12dp" tools:visibility="visible"
android:text="@string/medium" /> layout="@layout/tooltip_text_view"/>
</FrameLayout>
<com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/large" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/x_large" />
</RadioGroup>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/uniform_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="@string/uniform_covers" />
</LinearLayout>
</eu.kanade.tachiyomi.ui.library.display.LibraryDisplayView> </eu.kanade.tachiyomi.ui.library.display.LibraryDisplayView>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="45sp"
android:layout_height="45sp"
xmlns:tools="http://schemas.android.com/tools"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
tools:text="1"
android:elevation="6dp"
android:textAlignment="center"
android:paddingTop="8dp"
android:textColor="?colorOnAccent"
android:backgroundTint="?colorAccent"
android:background="@drawable/ic_rounded_tooltip_24dp" />

View File

@ -154,12 +154,14 @@
<string name="hide_start_reading_button">Hide start reading button</string> <string name="hide_start_reading_button">Hide start reading button</string>
<string name="badges">Badges</string> <string name="badges">Badges</string>
<string name="uniform_covers">Uniform covers</string> <string name="uniform_covers">Uniform covers</string>
<string name="uniform_grid_covers">Uniform grid covers</string>
<string name="grid_size">Grid size</string>
<string name="x_small">XS</string> <string name="x_small">XS</string>
<string name="small">S</string> <string name="small">S</string>
<string name="medium">M</string> <string name="medium">M</string>
<string name="large">L</string> <string name="large">L</string>
<string name="x_large">XL</string> <string name="x_large">XL</string>
<string name="grid_options">Grid options</string> <string name="_per_row">%d per row</string>
<string name="hide_unread_badges">Hide unread badges</string> <string name="hide_unread_badges">Hide unread badges</string>
<string name="show_unread_badges">Show unread badges</string> <string name="show_unread_badges">Show unread badges</string>
<string name="show_unread_count">Show unread count</string> <string name="show_unread_count">Show unread count</string>