feat(Zinnia): add configurable thresholds and previews for each size
Currently thresholds are unused, one day they should be hooked up to some formula based on real device pixels
This commit is contained in:
parent
e0b86e0fb4
commit
4fb5486a39
3 changed files with 89 additions and 83 deletions
|
|
@ -8,19 +8,25 @@ import com.aliucord.utils.DimenUtils.dp
|
||||||
import com.discord.stores.StoreStream
|
import com.discord.stores.StoreStream
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
enum class Threshold {
|
||||||
|
Large,
|
||||||
|
Medium,
|
||||||
|
Small
|
||||||
|
}
|
||||||
|
|
||||||
internal object APCAUtil {
|
internal object APCAUtil {
|
||||||
private val settings = ZinniaSettings
|
private val settings = ZinniaSettings
|
||||||
|
|
||||||
internal fun configureOn(view: TextView, colour: Int?) {
|
internal fun configureOn(view: TextView, colour: Int?, threshold: Threshold) {
|
||||||
when (settings.mode) {
|
when (settings.mode) {
|
||||||
Mode.Block -> configureBlock(view, colour ?: Color.BLACK)
|
Mode.Block -> configureBlock(view, colour ?: Color.BLACK, threshold)
|
||||||
Mode.RoleDot -> configureRoleDot(view, colour ?: Color.BLACK)
|
Mode.RoleDot -> configureRoleDot(view, colour ?: Color.BLACK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureRoleDot(view: TextView, colour: Int) { }
|
private fun configureRoleDot(view: TextView, colour: Int) { }
|
||||||
|
|
||||||
private fun configureBlock(view: TextView, colourP: Int) {
|
private fun configureBlock(view: TextView, colourP: Int, threshold: Threshold) {
|
||||||
val isLight = StoreStream.getUserSettingsSystem().theme == "light"
|
val isLight = StoreStream.getUserSettingsSystem().theme == "light"
|
||||||
var colour = colourP
|
var colour = colourP
|
||||||
val bcol = GradientDrawable()
|
val bcol = GradientDrawable()
|
||||||
|
|
@ -68,10 +74,10 @@ internal object APCAUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
val usePreferred = when (settings.blockMode) {
|
val usePreferred = when (settings.blockMode) {
|
||||||
BlockMode.ApcaOnly -> isApca(colours)
|
BlockMode.ApcaOnly -> isApca(colours, threshold)
|
||||||
BlockMode.WcagOnly -> isWcag(colours)
|
BlockMode.WcagOnly -> isWcag(colours)
|
||||||
BlockMode.ApcaLightWcagDark -> if (isLight) isApca(colours) else isWcag(colours)
|
BlockMode.ApcaLightWcagDark -> if (isLight) isApca(colours, threshold) else isWcag(colours)
|
||||||
BlockMode.WcagLightApcaDark -> if (isLight) isWcag(colours) else isApca(colours)
|
BlockMode.WcagLightApcaDark -> if (isLight) isWcag(colours) else isApca(colours, threshold)
|
||||||
BlockMode.ThemeOnly,
|
BlockMode.ThemeOnly,
|
||||||
BlockMode.InvertedThemeOnly,
|
BlockMode.InvertedThemeOnly,
|
||||||
BlockMode.WhiteOnly,
|
BlockMode.WhiteOnly,
|
||||||
|
|
@ -90,10 +96,15 @@ internal object APCAUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isApca(c: Colours): Boolean {
|
private fun isApca(c: Colours, threshold: Threshold): Boolean {
|
||||||
val cPref = abs(APCA.contrast(c.fgP, c.bgP))
|
val cPref = abs(APCA.contrast(c.fgP, c.bgP))
|
||||||
val cOth = abs(APCA.contrast(c.fgO, c.bgO))
|
val cOth = abs(APCA.contrast(c.fgO, c.bgO))
|
||||||
return cPref > settings.blockApcaThreshold || cPref > cOth
|
val thresholdValue = when (threshold) {
|
||||||
|
Threshold.Large -> settings.blockApcaThresholdLarge
|
||||||
|
Threshold.Medium -> settings.blockApcaThresholdMedium
|
||||||
|
Threshold.Small -> settings.blockApcaThresholdSmall
|
||||||
|
}
|
||||||
|
return cPref > thresholdValue || cPref > cOth
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isWcag(c: Colours): Boolean {
|
private fun isWcag(c: Colours): Boolean {
|
||||||
|
|
@ -101,5 +112,4 @@ internal object APCAUtil {
|
||||||
val cOth = ColorUtils.calculateContrast(c.fgO, c.bgO)
|
val cOth = ColorUtils.calculateContrast(c.fgO, c.bgO)
|
||||||
return cPref > settings.blockWcagThreshold || cPref > cOth
|
return cPref > settings.blockWcagThreshold || cPref > cOth
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ class Zinnia : Plugin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
APCAUtil.configureOn(usernameTextView, member.color)
|
APCAUtil.configureOn(usernameTextView, member.color, Threshold.Medium)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +75,7 @@ class Zinnia : Plugin() {
|
||||||
) { (_, _: Int, entry: MessageEntry) ->
|
) { (_, _: Int, entry: MessageEntry) ->
|
||||||
val username = itemView.findViewById<TextView?>("chat_list_adapter_item_text_name")
|
val username = itemView.findViewById<TextView?>("chat_list_adapter_item_text_name")
|
||||||
?: return@after
|
?: return@after
|
||||||
APCAUtil.configureOn(username, entry.author?.color)
|
APCAUtil.configureOn(username, entry.author?.color, Threshold.Large)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configures for reply preview username
|
// Configures for reply preview username
|
||||||
|
|
@ -86,7 +86,7 @@ class Zinnia : Plugin() {
|
||||||
val referencedAuthor = entry.replyData?.messageEntry?.author
|
val referencedAuthor = entry.replyData?.messageEntry?.author
|
||||||
val replyUsername = itemView.findViewById<TextView?>("chat_list_adapter_item_text_decorator_reply_name")
|
val replyUsername = itemView.findViewById<TextView?>("chat_list_adapter_item_text_decorator_reply_name")
|
||||||
?: return@after
|
?: return@after
|
||||||
APCAUtil.configureOn(replyUsername, referencedAuthor?.color)
|
APCAUtil.configureOn(replyUsername, referencedAuthor?.color, Threshold.Small)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package moe.lava.awoocord.zinnia
|
package moe.lava.awoocord.zinnia
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
|
@ -18,7 +19,6 @@ import com.aliucord.wrappers.users.globalName
|
||||||
import com.discord.stores.StoreStream
|
import com.discord.stores.StoreStream
|
||||||
import com.discord.utilities.color.ColorCompat
|
import com.discord.utilities.color.ColorCompat
|
||||||
import com.discord.views.CheckedSetting
|
import com.discord.views.CheckedSetting
|
||||||
import com.discord.views.RadioManager
|
|
||||||
import com.lytefast.flexinput.R
|
import com.lytefast.flexinput.R
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.properties.ReadWriteProperty
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
|
@ -94,22 +94,18 @@ object ZinniaSettings {
|
||||||
|
|
||||||
var mode by reactive { api.delegateEnum(Mode.Block) }
|
var mode by reactive { api.delegateEnum(Mode.Block) }
|
||||||
|
|
||||||
var dotKeepNameColour by reactive { api.delegate(false) }
|
|
||||||
|
|
||||||
var blockAlsoDefault by reactive { api.delegate(true) }
|
var blockAlsoDefault by reactive { api.delegate(true) }
|
||||||
var blockInverted by reactive { api.delegate(false) }
|
var blockInverted by reactive { api.delegate(false) }
|
||||||
var blockMode by reactive { api.delegateEnum(BlockMode.ApcaLightWcagDark) }
|
var blockMode by reactive { api.delegateEnum(BlockMode.ApcaLightWcagDark) }
|
||||||
var blockApcaThreshold by reactive { api.delegate(45.0f) }
|
var blockApcaThresholdLarge by reactive { api.delegate(45.0f) }
|
||||||
|
var blockApcaThresholdMedium by reactive { api.delegate(45.0f) }
|
||||||
|
var blockApcaThresholdSmall by reactive { api.delegate(45.0f) }
|
||||||
var blockWcagThreshold by reactive { api.delegate(4.5f) }
|
var blockWcagThreshold by reactive { api.delegate(4.5f) }
|
||||||
|
|
||||||
private val _alpha = reactive { api.delegate("alpha", 255) }
|
private val _alpha = reactive { api.delegate("alpha", 255) }
|
||||||
var alpha by _alpha
|
var alpha by _alpha
|
||||||
|
|
||||||
class Page : SettingsPage() {
|
class Page : SettingsPage() {
|
||||||
private lateinit var manager: RadioManager
|
|
||||||
private lateinit var mRoleDot: CheckedSetting
|
|
||||||
private lateinit var mBlock: CheckedSetting
|
|
||||||
|
|
||||||
private val checks = mutableListOf<CheckedSetting>()
|
private val checks = mutableListOf<CheckedSetting>()
|
||||||
|
|
||||||
private val _previewH = reactive { basicDelegate(0) }
|
private val _previewH = reactive { basicDelegate(0) }
|
||||||
|
|
@ -119,7 +115,7 @@ object ZinniaSettings {
|
||||||
private val _previewV = reactive { basicDelegate(100) }
|
private val _previewV = reactive { basicDelegate(100) }
|
||||||
private var previewV by _previewV
|
private var previewV by _previewV
|
||||||
|
|
||||||
private fun createRadio(newMode: BlockMode, text: String, subtext: String? = null): CheckedSetting {
|
private fun addRadio(newMode: BlockMode, text: String, subtext: String? = null): CheckedSetting {
|
||||||
return Utils.createCheckedSetting(requireContext(), CheckedSetting.ViewType.RADIO, text, subtext).addTo(linearLayout) {
|
return Utils.createCheckedSetting(requireContext(), CheckedSetting.ViewType.RADIO, text, subtext).addTo(linearLayout) {
|
||||||
isChecked = blockMode == newMode
|
isChecked = blockMode == newMode
|
||||||
setOnCheckedListener {
|
setOnCheckedListener {
|
||||||
|
|
@ -131,7 +127,18 @@ object ZinniaSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSlider(
|
private fun createLabel(text: String? = null): TextView {
|
||||||
|
return TextView(context, null, 0, R.i.UiKit_TextView).apply {
|
||||||
|
textSize = 16.0f
|
||||||
|
typeface = ResourcesCompat.getFont(context, Constants.Fonts.whitney_medium)
|
||||||
|
this.text = text
|
||||||
|
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
||||||
|
bottomMargin = 4.dp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addSlider(
|
||||||
min: Int,
|
min: Int,
|
||||||
max: Int,
|
max: Int,
|
||||||
initial: Int = min,
|
initial: Int = min,
|
||||||
|
|
@ -140,14 +147,7 @@ object ZinniaSettings {
|
||||||
var pendingValue = initial
|
var pendingValue = initial
|
||||||
return LinearLayout(requireContext(), null, 0, R.i.UiKit_Settings_Item).addTo(linearLayout) {
|
return LinearLayout(requireContext(), null, 0, R.i.UiKit_Settings_Item).addTo(linearLayout) {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
val display = TextView(context, null, 0, R.i.UiKit_TextView).addTo(this) {
|
val display = createLabel(onChange(initial, false)).addTo(this)
|
||||||
textSize = 16.0f
|
|
||||||
typeface = ResourcesCompat.getFont(context, Constants.Fonts.whitney_medium)
|
|
||||||
text = onChange(initial, false)
|
|
||||||
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
|
||||||
bottomMargin = 4.dp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SeekBar(context, null, 0, R.i.UiKit_SeekBar).addTo(this) {
|
SeekBar(context, null, 0, R.i.UiKit_SeekBar).addTo(this) {
|
||||||
this.max = max - min
|
this.max = max - min
|
||||||
progress = initial
|
progress = initial
|
||||||
|
|
@ -171,15 +171,39 @@ object ZinniaSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSlider(binding: Delegate<Int>, min: Int, max: Int, immediate: Boolean = false, label: (Int) -> String): LinearLayout {
|
private fun addSlider(binding: Delegate<Int>, min: Int, max: Int, immediate: Boolean = false, label: (Int) -> String): LinearLayout {
|
||||||
var value by binding
|
var value by binding
|
||||||
return createSlider(min, max, value) { newValue, commit ->
|
return addSlider(min, max, value) { newValue, commit ->
|
||||||
@Suppress("AssignedValueIsNeverRead") // kt so dumb
|
@Suppress("AssignedValueIsNeverRead") // kt so dumb
|
||||||
if (immediate || commit) value = newValue
|
if (immediate || commit) value = newValue
|
||||||
label(newValue)
|
label(newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createPreview(
|
||||||
|
label: String,
|
||||||
|
styleRes: Int,
|
||||||
|
): TextView {
|
||||||
|
val ctx = requireContext()
|
||||||
|
val view = TextView(ctx, null, 0, styleRes).apply {
|
||||||
|
val me = StoreStream.getUsers().me
|
||||||
|
text = me.globalName ?: me.username
|
||||||
|
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
||||||
|
marginStart = 16.dp
|
||||||
|
marginEnd = 16.dp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LinearLayout(ctx, null, 0, R.i.UiKit_Settings_Item).addTo(linearLayout) {
|
||||||
|
view.addTo(this)
|
||||||
|
createLabel(label).addTo(this) {
|
||||||
|
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
||||||
|
bottomMargin = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
onStateUpdate = {}
|
onStateUpdate = {}
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
|
@ -196,42 +220,12 @@ object ZinniaSettings {
|
||||||
val roleDotSettings = mutableListOf<CheckedSetting>()
|
val roleDotSettings = mutableListOf<CheckedSetting>()
|
||||||
|
|
||||||
addHeader(ctx, "Text colour")
|
addHeader(ctx, "Text colour")
|
||||||
createRadio(BlockMode.ApcaLightWcagDark, "Automatic", "Adjusts text colour based on optimal contrast with role colour")
|
addRadio(BlockMode.ApcaLightWcagDark, "Automatic", "Adjusts text colour based on optimal contrast with role colour")
|
||||||
createRadio(BlockMode.ThemeOnly, "By theme", "Adjusts text colour based on system theme (dark/light)")
|
addRadio(BlockMode.ThemeOnly, "By theme", "Adjusts text colour based on system theme (dark/light)")
|
||||||
createRadio(BlockMode.InvertedThemeOnly, "By theme (inverted)", "Same as above, but inverted")
|
addRadio(BlockMode.InvertedThemeOnly, "By theme (inverted)", "Same as above, but inverted")
|
||||||
createRadio(BlockMode.WhiteOnly, "White", "Force text colour to be white")
|
addRadio(BlockMode.WhiteOnly, "White", "Force text colour to be white")
|
||||||
createRadio(BlockMode.BlackOnly, "Black", "Force text colour to be black")
|
addRadio(BlockMode.BlackOnly, "Black", "Force text colour to be black")
|
||||||
createRadio(BlockMode.Unchanged, "Unchanged", "Keep text colour; ideal for using with a translucent block")
|
addRadio(BlockMode.Unchanged, "Unchanged", "Keep text colour; ideal for using with a translucent block")
|
||||||
|
|
||||||
/*
|
|
||||||
addHeader(ctx, "Mode")
|
|
||||||
|
|
||||||
mBlock = Utils.createCheckedSetting(
|
|
||||||
ctx,
|
|
||||||
CheckedSetting.ViewType.RADIO,
|
|
||||||
"Block mode",
|
|
||||||
"Wraps the username in a coloured block",
|
|
||||||
).addTo(this) {
|
|
||||||
isChecked = mode == Mode.Block
|
|
||||||
setOnCheckedListener {
|
|
||||||
mode = Mode.Block
|
|
||||||
mRoleDot.isChecked = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mRoleDot = Utils.createCheckedSetting(
|
|
||||||
ctx,
|
|
||||||
CheckedSetting.ViewType.RADIO,
|
|
||||||
"Role dot mode",
|
|
||||||
"Adds a coloured role dot next to the username, similar to how Discord does it in their new accessibility settings",
|
|
||||||
).addTo(this) {
|
|
||||||
isChecked = mode == Mode.RoleDot
|
|
||||||
setOnCheckedListener {
|
|
||||||
mode = Mode.RoleDot
|
|
||||||
mBlock.isChecked = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
addHeader(ctx, "Block Settings")
|
addHeader(ctx, "Block Settings")
|
||||||
|
|
||||||
|
|
@ -248,7 +242,7 @@ object ZinniaSettings {
|
||||||
blockSettings.add(this)
|
blockSettings.add(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
createSlider(_alpha, 0, 255, true) { "Alpha: ${(it / 2.55f).roundToInt()}%" }
|
addSlider(_alpha, 0, 255, true) { "Alpha: ${(it / 2.55f).roundToInt()}%" }
|
||||||
|
|
||||||
// createSlider(0, 255, blockApcaThreshold.roundToInt()) { value, commit ->
|
// createSlider(0, 255, blockApcaThreshold.roundToInt()) { value, commit ->
|
||||||
// blockApcaThreshold = value.toFloat()
|
// blockApcaThreshold = value.toFloat()
|
||||||
|
|
@ -256,26 +250,27 @@ object ZinniaSettings {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
addHeader(ctx, "Preview")
|
addHeader(ctx, "Preview")
|
||||||
val preview = TextView(ctx, null, 0, R.i.UiKit_TextView_Large_SingleLine).addTo(this) {
|
val previews = mutableListOf(
|
||||||
val me = StoreStream.getUsers().me
|
Threshold.Large to createPreview("Message header username", R.i.UiKit_TextView_Large_SingleLine),
|
||||||
text = me.globalName ?: me.username
|
Threshold.Medium to createPreview("Channels list", R.i.UiKit_TextView).apply {
|
||||||
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
|
setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.d.uikit_textsize_medium))
|
||||||
marginStart = 16.dp
|
},
|
||||||
marginEnd = 16.dp
|
Threshold.Small to createPreview("Message reply username", R.i.UiKit_TextView).apply {
|
||||||
}
|
setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.d.uikit_textsize_small))
|
||||||
}
|
},
|
||||||
|
)
|
||||||
|
|
||||||
val hsv = floatArrayOf(0f, 0f, 0f)
|
val hsv = floatArrayOf(0f, 0f, 0f)
|
||||||
Color.colorToHSV(ColorCompat.getThemedColor(this, R.b.color_brand), hsv)
|
Color.colorToHSV(ColorCompat.getThemedColor(this, R.b.color_brand), hsv)
|
||||||
previewH = hsv[0].roundToInt()
|
previewH = hsv[0].roundToInt()
|
||||||
previewS = (hsv[1] * 100).roundToInt()
|
previewS = (hsv[1] * 100).roundToInt()
|
||||||
previewV = (hsv[2] * 100).roundToInt()
|
previewV = (hsv[2] * 100).roundToInt()
|
||||||
createSlider(_previewH, 0, 360, true) { "Hue: $it" }
|
addSlider(_previewH, 0, 360, true) { "Hue: $it" }
|
||||||
createSlider(_previewS, 0, 100, true) { "Saturation: $it%" }
|
addSlider(_previewS, 0, 100, true) { "Saturation: $it%" }
|
||||||
createSlider(_previewV, 0, 100, true) { "Value: $it%" }
|
addSlider(_previewV, 0, 100, true) { "Value: $it%" }
|
||||||
|
|
||||||
onStateUpdate = {
|
onStateUpdate = {
|
||||||
updatePreview(preview)
|
previews.forEach { updatePreview(it) }
|
||||||
if (blockMode != BlockMode.Unchanged) {
|
if (blockMode != BlockMode.Unchanged) {
|
||||||
invertSwitch.l.b().isClickable = true
|
invertSwitch.l.b().isClickable = true
|
||||||
invertSwitch.alpha = 1f
|
invertSwitch.alpha = 1f
|
||||||
|
|
@ -288,9 +283,10 @@ object ZinniaSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePreview(preview: TextView) {
|
fun updatePreview(pair: Pair<Threshold, TextView>) {
|
||||||
|
val (threshold, preview) = pair
|
||||||
val colour = Color.HSVToColor(floatArrayOf(previewH.toFloat(), previewS / 100f, previewV / 100f))
|
val colour = Color.HSVToColor(floatArrayOf(previewH.toFloat(), previewS / 100f, previewV / 100f))
|
||||||
APCAUtil.configureOn(preview, colour)
|
APCAUtil.configureOn(preview, colour, threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue