Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.swmansion.rnscreens.gamma.stack.header

import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.util.Log
Expand All @@ -13,6 +14,7 @@ import androidx.appcompat.view.ContextThemeWrapper
import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.widget.TextViewCompat
import com.google.android.material.R
import com.google.android.material.appbar.AppBarLayout
Expand Down Expand Up @@ -65,7 +67,9 @@ internal class StackHeaderCoordinator(
private var lastBackgroundSubviewCollapseMode: StackHeaderSubviewCollapseMode? = null

private var lastBackButtonVisible: Boolean? = null
private var lastBackButtonTintColor: Int? = null
private var lastBackButtonTintColorNormal: Int? = null
private var lastBackButtonTintColorPressed: Int? = null
private var lastBackButtonTintColorFocused: Int? = null
private var lastBackButtonIcon: Drawable? = null

private var lastScrollFlags: Int? = null
Expand Down Expand Up @@ -171,7 +175,9 @@ internal class StackHeaderCoordinator(
appBarLayout = null
managedTitleView = null
lastBackButtonVisible = null
lastBackButtonTintColor = null
lastBackButtonTintColorNormal = null
lastBackButtonTintColorPressed = null
lastBackButtonTintColorFocused = null
lastBackButtonIcon = null
lastScrollFlags = null
clearCachedRebuildTriggers()
Expand Down Expand Up @@ -424,32 +430,71 @@ internal class StackHeaderCoordinator(
val visible = canNavigateBack && !config.backButtonHidden
val visibilityChanged = visible != lastBackButtonVisible
val iconChanged = config.backButtonIcon !== lastBackButtonIcon
val tintChanged = config.backButtonTintColor != lastBackButtonTintColor
val tintChanged =
config.backButtonTintColorNormal != lastBackButtonTintColorNormal ||
config.backButtonTintColorPressed != lastBackButtonTintColorPressed ||
config.backButtonTintColorFocused != lastBackButtonTintColorFocused

if (!visibilityChanged && !iconChanged && !tintChanged) return

lastBackButtonVisible = visible
lastBackButtonIcon = config.backButtonIcon
lastBackButtonTintColor = config.backButtonTintColor
lastBackButtonTintColorNormal = config.backButtonTintColorNormal
lastBackButtonTintColorPressed = config.backButtonTintColorPressed
lastBackButtonTintColorFocused = config.backButtonTintColorFocused

if (!visible) {
toolbar.navigationIcon = null
toolbar.setNavigationOnClickListener(null)
return
}

// Clear previous tint before setting icon to ensure clean state
toolbar.clearNavigationIconTint()

toolbar.navigationIcon = config.backButtonIcon ?: resolveDefaultBackButtonIcon()
val baseDrawable =
config.backButtonIcon
?.let { getResizedDrawable(toolbar, it) }
?: resolveDefaultBackButtonIcon()

config.backButtonTintColor?.let {
toolbar.setNavigationIconTint(it)
}
val tintList = resolveBackButtonTintList(config)
toolbar.navigationIcon =
if (tintList != null && baseDrawable != null) {
DrawableCompat.wrap(baseDrawable.mutate()).also {
DrawableCompat.setTintList(it, tintList)
}
} else {
baseDrawable
}

toolbar.setNavigationOnClickListener { onNavigationIconClick() }
}

private fun resolveBackButtonTintList(config: StackHeaderConfigProviding): ColorStateList? {
val normal = config.backButtonTintColorNormal
val pressed = config.backButtonTintColorPressed
val focused = config.backButtonTintColorFocused

if (normal == null && pressed == null && focused == null) return null

val states = mutableListOf<IntArray>()
val colors = mutableListOf<Int>()

pressed?.let {
states.add(intArrayOf(android.R.attr.state_pressed))
colors.add(it)
}
focused?.let {
states.add(intArrayOf(android.R.attr.state_focused))
colors.add(it)
}
normal?.let {
states.add(intArrayOf())
colors.add(it)
}

return ColorStateList(states.toTypedArray(), colors.toIntArray())
}
Comment thread
kligarski marked this conversation as resolved.

// endregion

// region Content behavior
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.swmansion.rnscreens.gamma.stack.header

import android.graphics.drawable.Drawable
import android.util.TypedValue
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import com.google.android.material.appbar.MaterialToolbar

/**
* Returns [drawable] resized to 24 dp height. Width is scaled proportionally to keep the aspect
* ratio.
*
* Icon size source: https://m3.material.io/components/app-bars/specs - App bar icon size
*/
internal fun getResizedDrawable(
toolbar: MaterialToolbar,
drawable: Drawable,
): Drawable {
val targetHeightPx =
TypedValue
.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
24f,
toolbar.resources.displayMetrics,
).toInt()

val intrinsicWidth = drawable.intrinsicWidth
val intrinsicHeight = drawable.intrinsicHeight

val targetWidthPx =
if (intrinsicWidth > 0 && intrinsicHeight > 0) {
val aspectRatio = intrinsicWidth.toFloat() / intrinsicHeight.toFloat()
(targetHeightPx * aspectRatio).toInt()
} else {
targetHeightPx
}

val bitmap = drawable.toBitmap(width = targetWidthPx, height = targetHeightPx)
bitmap.density = toolbar.resources.displayMetrics.densityDpi
return bitmap.toDrawable(toolbar.resources)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ class StackHeaderConfig(
internal set
override var backButtonHidden: Boolean = false
internal set
override var backButtonTintColor: Int? = null
override var backButtonTintColorNormal: Int? = null
internal set
override var backButtonTintColorPressed: Int? = null
internal set
override var backButtonTintColorFocused: Int? = null
internal set
override var backButtonIcon: Drawable? = null
internal set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ interface StackHeaderConfigProviding {
val hidden: Boolean
val transparent: Boolean
val backButtonHidden: Boolean
val backButtonTintColor: Int?
val backButtonTintColorNormal: Int?
val backButtonTintColorPressed: Int?
val backButtonTintColorFocused: Int?
val backButtonIcon: Drawable?
val scrollFlagScroll: Boolean
val scrollFlagEnterAlways: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,25 @@ open class StackHeaderConfigViewManager :
view.backButtonHidden = value
}

override fun setBackButtonTintColor(
override fun setBackButtonTintColorNormal(
view: StackHeaderConfig,
value: Int?,
) {
view.backButtonTintColor = value
view.backButtonTintColorNormal = value
}

override fun setBackButtonTintColorPressed(
view: StackHeaderConfig,
value: Int?,
) {
view.backButtonTintColorPressed = value
}

override fun setBackButtonTintColorFocused(
view: StackHeaderConfig,
value: Int?,
) {
view.backButtonTintColorFocused = value
}

override fun setBackButtonDrawableIconResourceName(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package com.swmansion.rnscreens.gamma.stack.header.toolbar
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.util.Log
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.MenuItemCompat
import com.google.android.material.appbar.MaterialToolbar
import com.swmansion.rnscreens.gamma.stack.header.getResizedDrawable

internal class StackHeaderToolbarMenuCoordinator(
private val onItemClicked: (id: String) -> Unit,
Expand Down Expand Up @@ -100,40 +98,6 @@ internal class StackHeaderToolbarMenuCoordinator(
iconTintColorDisabled = StackHeaderToolbarUpdate.from(iconTintColorDisabled),
)

/**
* Returns drawable resized to 24 dp height. Width is scaled proportionally to keep the aspect
* ratio.
*
* Icon size source: https://m3.material.io/components/app-bars/specs - App bar icon size
*/
private fun getResizedDrawable(
toolbar: MaterialToolbar,
drawable: Drawable,
): Drawable {
val targetHeightPx =
TypedValue
.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
24f,
toolbar.resources.displayMetrics,
).toInt()

val intrinsicWidth = drawable.intrinsicWidth
val intrinsicHeight = drawable.intrinsicHeight

val targetWidthPx =
if (intrinsicWidth > 0 && intrinsicHeight > 0) {
val aspectRatio = intrinsicWidth.toFloat() / intrinsicHeight.toFloat()
(targetHeightPx * aspectRatio).toInt()
} else {
targetHeightPx
}

return drawable
.toBitmap(width = targetWidthPx, height = targetHeightPx)
.toDrawable(toolbar.resources)
}

private fun getResolvedIconTintList(
menuItem: MenuItem,
options: StackHeaderToolbarMenuItemOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@ const ICON_OPTIONS: IconOption[] = [

interface Config {
backButtonHidden: boolean;
tintColor: TintColorOption;
tintColorNormal: TintColorOption;
tintColorPressed: TintColorOption;
tintColorFocused: TintColorOption;
icon: IconOption;
}

const DEFAULT_CONFIG: Config = {
backButtonHidden: false,
tintColor: 'default',
tintColorNormal: 'default',
tintColorPressed: 'default',
tintColorFocused: 'default',
icon: 'default',
};

Expand All @@ -51,7 +55,7 @@ const ConfigContext = React.createContext<{

function resolveTintColor(
option: TintColorOption,
): StackHeaderConfigPropsAndroid['backButtonTintColor'] {
): StackHeaderConfigPropsAndroid['backButtonTintColorNormal'] {
switch (option) {
case 'purple':
return Colors.PurpleLight100;
Expand Down Expand Up @@ -88,7 +92,9 @@ function buildHeaderConfig(config: Config): StackHeaderConfigProps {
title: 'Back Button Test',
backButtonHidden: config.backButtonHidden,
android: {
backButtonTintColor: resolveTintColor(config.tintColor),
backButtonTintColorNormal: resolveTintColor(config.tintColorNormal),
backButtonTintColorPressed: resolveTintColor(config.tintColorPressed),
backButtonTintColorFocused: resolveTintColor(config.tintColorFocused),
backButtonIcon: resolveIcon(config.icon),
},
};
Expand Down Expand Up @@ -136,9 +142,21 @@ function ConfigControls() {
onValueChange={v => updateConfig('backButtonHidden', v)}
/>
<SettingsPicker<TintColorOption>
label="tintColor"
value={config.tintColor}
onValueChange={v => updateConfig('tintColor', v)}
label="tintColorNormal"
value={config.tintColorNormal}
onValueChange={v => updateConfig('tintColorNormal', v)}
items={TINT_COLOR_OPTIONS}
/>
<SettingsPicker<TintColorOption>
label="tintColorPressed"
value={config.tintColorPressed}
onValueChange={v => updateConfig('tintColorPressed', v)}
items={TINT_COLOR_OPTIONS}
/>
<SettingsPicker<TintColorOption>
label="tintColorFocused"
value={config.tintColorFocused}
onValueChange={v => updateConfig('tintColorFocused', v)}
items={TINT_COLOR_OPTIONS}
/>
<SettingsPicker<IconOption>
Expand Down
Loading