Fix focused card captions and leave loading bands blank
All checks were successful
review/code-claude Blocker fixed on c2f7ccc4, delta reviewed
review/design-claude Blocker fixed on c2f7ccc4, delta reviewed
All checks were successful
review/code-claude Blocker fixed on c2f7ccc4, delta reviewed
review/design-claude Blocker fixed on c2f7ccc4, delta reviewed
This commit is contained in:
parent
fe44a3c24f
commit
c2f7ccc438
11 changed files with 248 additions and 44 deletions
|
|
@ -31,7 +31,7 @@ fun CardCaption(title: String, year: Int?, duration: String?, watched: Boolean,
|
|||
val entryProgress = remember(title) { mutableFloatStateOf(0f) }
|
||||
val reduced = reducedMotion()
|
||||
val rise = with(LocalDensity.current) { WolfskinTokens.Motion.captionRise.toPx() }
|
||||
LaunchedEffect(focused, reduced) {
|
||||
LaunchedEffect(alpha, focused, reduced) {
|
||||
if (focused && !reduced) kotlinx.coroutines.delay(WolfskinTokens.Motion.captionDelay.toLong())
|
||||
alpha.animateTo(if (focused) 1f else 0f, tween(motionDuration(if (focused) WolfskinTokens.Motion.focusMove else WolfskinTokens.Motion.captionOut, reduced), easing = WolfskinTokens.Motion.easeOut)) {
|
||||
if (focused) entryProgress.floatValue = value
|
||||
|
|
|
|||
|
|
@ -54,16 +54,18 @@ fun HomeShelf(row: HomeRow, memory: HomeViewMemory, focused: Boolean, column: In
|
|||
val cardModifier = Modifier.zIndex(if (selected) 2f else 0f).testTag("home-card:${row.id}:$index")
|
||||
.then(if (!row.skeleton) Modifier.focusOnPointerEnter { onHover(index) } else Modifier)
|
||||
if (row.skeleton) {
|
||||
PosterCaptionSkeleton(width, height, caption, row.cardKind == HomeCardKind.Poster)
|
||||
PosterCaptionSkeleton(width, height, caption)
|
||||
} else if (title == null) {
|
||||
LibrarySeeAllCard(row.total, selected, { onCard(index) }, cardModifier, width, height, T.listTitle, T.label)
|
||||
} else TitleCard(selected, cardModifier.clickable { onCard(index) }, width, height,
|
||||
watched = !row.catalogue && title.played,
|
||||
progress = if (row.catalogue || title.runtimeSeconds == null || title.runtimeSeconds <= 0 || title.resumeSeconds <= 0) 0f else (title.resumeSeconds / title.runtimeSeconds).toFloat()) {
|
||||
TitleArtwork(title.title, title.type, title.poster, width, height, Modifier.fillMaxSize(), backdrop = title.backdrop, posterOnly = title.posterOnly, landscape = row.cardKind != HomeCardKind.Poster)
|
||||
if (row.cardKind != HomeCardKind.Poster) TileOverlay(title)
|
||||
if (row.catalogue && title.held) Box(Modifier.align(Alignment.BottomEnd).padding(L.badgeInset).size(L.badge).background(C.ok, CircleShape), contentAlignment = Alignment.Center) { Text("✓", color = C.onFocus, style = T.button) }
|
||||
HomeRatingPlate(if (selected) ratings else emptyList(), selected, Modifier.fillMaxWidth())
|
||||
} else {
|
||||
val progress = if (row.catalogue || title.runtimeSeconds == null || title.runtimeSeconds <= 0 || title.resumeSeconds <= 0) 0f else (title.resumeSeconds / title.runtimeSeconds).toFloat()
|
||||
TitleCard(selected, cardModifier.clickable { onCard(index) }, width, height,
|
||||
watched = !row.catalogue && title.played, progress = if (progress >= 1f) 0f else progress) {
|
||||
TitleArtwork(title.title, title.type, title.poster, width, height, Modifier.fillMaxSize(), backdrop = title.backdrop, posterOnly = title.posterOnly, landscape = row.cardKind != HomeCardKind.Poster)
|
||||
if (row.cardKind != HomeCardKind.Poster) TileOverlay(title)
|
||||
if (row.catalogue && title.held) Box(Modifier.align(Alignment.BottomEnd).padding(L.badgeInset).size(L.badge).background(C.ok, CircleShape), contentAlignment = Alignment.Center) { Text("✓", color = C.onFocus, style = T.button) }
|
||||
HomeRatingPlate(if (selected) ratings else emptyList(), selected, Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,18 +56,12 @@ private fun SkeletonBlock(width: androidx.compose.ui.unit.Dp, height: androidx.c
|
|||
Box(Modifier.size(width, height).background(C.surface2, RoundedCornerShape(WolfskinTokens.Radius.card)))
|
||||
}
|
||||
|
||||
/** Loading and real grid rows reserve the identical two-line caption band. */
|
||||
/** Loading reserves the ready caption band without painting unapproved text placeholders. */
|
||||
@Composable
|
||||
internal fun PosterCaptionSkeleton(width: androidx.compose.ui.unit.Dp = K.posterW,
|
||||
height: androidx.compose.ui.unit.Dp = K.posterH, band: androidx.compose.ui.unit.Dp = K.posterCaptionBand,
|
||||
showTitle: Boolean = true) {
|
||||
height: androidx.compose.ui.unit.Dp = K.posterH, band: androidx.compose.ui.unit.Dp = K.posterCaptionBand) {
|
||||
Column(Modifier.width(width)) {
|
||||
SkeletonBlock(width, height)
|
||||
Box(Modifier.height(band)) {
|
||||
Column(Modifier.offset(y = L.captionOffset)) {
|
||||
if (showTitle) SkeletonBlock(width, with(androidx.compose.ui.platform.LocalDensity.current) { WolfskinTokens.CardMetadata.titleRest.lineHeight.toDp() })
|
||||
SkeletonBlock(width / 2, with(androidx.compose.ui.platform.LocalDensity.current) { WolfskinTokens.CardMetadata.subtitle.lineHeight.toDp() })
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(band))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,13 +187,11 @@ object WolfskinTokens {
|
|||
val tileH = 112.5.dp
|
||||
val tileGap = 20.0.dp
|
||||
val tilePitch = 220.0.dp
|
||||
val tileCaptionBand = 32.0.dp
|
||||
const val tileSlots = 4
|
||||
val wideW = 272.0.dp
|
||||
val wideH = 153.0.dp
|
||||
val wideGap = 20.0.dp
|
||||
val widePitch = 292.0.dp
|
||||
val wideCaptionBand = 32.0.dp
|
||||
const val wideSlots = 3
|
||||
val entryW = 128.0.dp
|
||||
val entryH = 72.0.dp
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.takahirom.roborazzi.captureRoboImage
|
||||
import java.io.File
|
||||
|
|
@ -83,4 +84,52 @@ class CardBottomPixelTest {
|
|||
bitmap.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun completedHomeProgressUsesTheSameWatchedStripAsLibraryCards() {
|
||||
rule.activity.window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
@Suppress("DEPRECATION")
|
||||
rule.activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
val path = "/library/assets/movie-annie-hall-1977-poster.jpg"
|
||||
val fraction = mutableStateOf(.5f)
|
||||
val art = mapOf(path to fixtureArt(path)!!)
|
||||
val memory = HomeViewMemory()
|
||||
rule.setContent { WolfskinTheme {
|
||||
androidx.compose.runtime.CompositionLocalProvider(LocalTitleBitmaps provides art) {
|
||||
Box(Modifier.requiredSize(960.dp, 540.dp).background(WolfskinTokens.Color.bg)) {
|
||||
val title = HomeTitle("home", "Home film", "movie", runtimeSeconds = 100,
|
||||
poster = path, played = true, resumeSeconds = fraction.value * 100.0)
|
||||
HomeShelf(HomeRow("progress", HomeRowKind.Shelf, "Progress", listOf(title)),
|
||||
memory, false, 0, emptyList(), {}, {})
|
||||
LibraryPoster(LibraryTitle("reference", "Reference", "movie", poster = path,
|
||||
played = true, progress = fraction.value), false, false, false,
|
||||
Modifier.offset(240.dp, 42.dp), ::fixtureArt)
|
||||
}
|
||||
}
|
||||
} }
|
||||
val directory = File(System.getProperty("wolfskin.captureDirectory"), "card-pixels").apply { mkdirs() }
|
||||
for (progress in listOf(.5f, 1f, 1.5f)) {
|
||||
rule.runOnIdle { fraction.value = progress }
|
||||
rule.mainClock.advanceTimeBy(1000)
|
||||
rule.waitForIdle()
|
||||
val home = rule.onNodeWithTag("home-card:progress:0").fetchSemanticsNode().boundsInRoot
|
||||
val reference = rule.onNodeWithTag("poster-reference").fetchSemanticsNode().boundsInRoot
|
||||
assertEquals(reference.width, home.width, .1f)
|
||||
assertEquals(reference.bottom, home.bottom, .1f)
|
||||
val file = File(directory, "home-progress-${(progress * 100).toInt()}.png")
|
||||
rule.onRoot().captureRoboImage(file.absolutePath)
|
||||
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
|
||||
try {
|
||||
val bottom = home.bottom.toInt()
|
||||
val band = if (progress >= 1f) 7 else 10
|
||||
for (y in bottom - band until bottom) for (x in 0 until home.width.toInt()) {
|
||||
assertEquals("Home/library strip progress=$progress ($x,$y)",
|
||||
bitmap.getPixel(reference.left.toInt() + x, y), bitmap.getPixel(home.left.toInt() + x, y))
|
||||
}
|
||||
val separator = if (progress >= 1f) WolfskinTokens.Color.bg else WolfskinTokens.Color.accent
|
||||
assertEquals("Completion changes the resume strip to the 3px watched strip",
|
||||
separator.toArgb(), bitmap.getPixel(home.left.toInt() + 32, bottom - 7))
|
||||
assertEquals(WolfskinTokens.Color.accent.toArgb(), bitmap.getPixel(home.left.toInt() + 32, bottom - 4))
|
||||
} finally { bitmap.recycle() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
package org.justinvisser.wolfskin.design
|
||||
|
||||
import android.app.Application
|
||||
import android.graphics.BitmapFactory
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.snapshots.Snapshot
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.test.*
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.takahirom.roborazzi.captureRoboImage
|
||||
import java.io.File
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.annotation.GraphicsMode
|
||||
|
||||
/** Changing a shelf's selected identity must restart its focus-only caption reveal. */
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34], application = Application::class, qualifiers = "w960dp-h540dp-xhdpi")
|
||||
@GraphicsMode(GraphicsMode.Mode.NATIVE)
|
||||
class CardCaptionMotionTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Before fun fullscreen() {
|
||||
rule.activity.window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
@Suppress("DEPRECATION")
|
||||
rule.activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
rule.mainClock.autoAdvance = false
|
||||
}
|
||||
|
||||
@Test fun changingTitleWhileStillFocusedRestoresOpaqueCaptionPixels() {
|
||||
val title = mutableStateOf("Initial caption")
|
||||
rule.setContent { WolfskinTheme {
|
||||
Box(Modifier.requiredSize(960.dp, 540.dp).background(WolfskinTokens.Color.bg)) {
|
||||
CardCaption(title.value, 2007, "2h 56m", true, true,
|
||||
Modifier.offset(40.dp, 80.dp).width(128.dp).testTag("animated"), focusOnly = true)
|
||||
CardCaption(title.value, 2007, "2h 56m", true, true,
|
||||
Modifier.offset(40.dp, 180.dp).width(128.dp).testTag("reference"))
|
||||
}
|
||||
} }
|
||||
for ((index, selected) in listOf("Initial caption", "Second caption", "Third caption").withIndex()) {
|
||||
update { title.value = selected }
|
||||
rule.mainClock.advanceTimeBy(400)
|
||||
assertCaptionPixels("component-$index",
|
||||
rule.onNodeWithTag("animated").fetchSemanticsNode().boundsInRoot,
|
||||
rule.onNodeWithTag("reference").fetchSemanticsNode().boundsInRoot)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun movingAcrossAFocusedHomeShelfKeepsEachSelectedCaptionVisible() {
|
||||
val path = "/library/assets/movie-annie-hall-1977-poster.jpg"
|
||||
val cards = listOf(HomeTitle("first", "First shelf caption", "movie", 2007, 10560, poster = path),
|
||||
HomeTitle("second", "Second shelf caption", "movie", 2008, 11520, poster = path))
|
||||
val row = HomeRow("captions", HomeRowKind.Shelf, "Shelf", cards)
|
||||
val column = mutableStateOf(0)
|
||||
val memory = HomeViewMemory()
|
||||
val art = mapOf(path to fixtureArt(path)!!)
|
||||
rule.setContent { WolfskinTheme {
|
||||
CompositionLocalProvider(LocalTitleBitmaps provides art) {
|
||||
Box(Modifier.requiredSize(960.dp, 540.dp).background(WolfskinTokens.Color.bg)) {
|
||||
Box(Modifier.testTag("home-shelf")) {
|
||||
HomeShelf(row, memory, true, column.value, emptyList(), {}, {})
|
||||
}
|
||||
val selected = cards[column.value]
|
||||
CardCaption(selected.title, selected.year, selected.runtime, false, true,
|
||||
Modifier.offset(48.dp, 400.dp).width(128.dp).testTag("reference"))
|
||||
}
|
||||
}
|
||||
} }
|
||||
for ((step, selected) in listOf(0, 1, 0).withIndex()) {
|
||||
update { column.value = selected }
|
||||
rule.mainClock.advanceTimeBy(400)
|
||||
val title = cards[selected].title
|
||||
val actual = rule.onNode(hasText(title) and hasAnyAncestor(hasTestTag("home-shelf")), useUnmergedTree = true).fetchSemanticsNode().boundsInRoot
|
||||
val reference = rule.onNode(hasText(title) and hasAnyAncestor(hasTestTag("reference")), useUnmergedTree = true).fetchSemanticsNode().boundsInRoot
|
||||
assertCaptionPixels("home-$step", actual, reference)
|
||||
}
|
||||
}
|
||||
|
||||
private fun update(block: () -> Unit) {
|
||||
rule.runOnIdle { block(); Snapshot.sendApplyNotifications() }
|
||||
rule.mainClock.advanceTimeByFrame()
|
||||
}
|
||||
|
||||
private fun assertCaptionPixels(name: String, actual: Rect, reference: Rect) {
|
||||
assertEquals(reference.width, actual.width, .1f)
|
||||
assertEquals(reference.height, actual.height, .1f)
|
||||
rule.waitForIdle()
|
||||
val directory = File(System.getProperty("wolfskin.captureDirectory"), "caption-followup").apply { mkdirs() }
|
||||
val file = File(directory, "$name.png")
|
||||
rule.onRoot().captureRoboImage(file.absolutePath)
|
||||
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
|
||||
try {
|
||||
val foreground = WolfskinTokens.Color.fg.toArgb()
|
||||
var compared = 0
|
||||
for (y in 0 until reference.height.toInt()) for (x in 0 until reference.width.toInt()) {
|
||||
if (bitmap.getPixel(reference.left.toInt() + x, reference.top.toInt() + y) == foreground) {
|
||||
assertEquals("$name opaque title pixel ($x,$y)", foreground,
|
||||
bitmap.getPixel(actual.left.toInt() + x, actual.top.toInt() + y))
|
||||
compared++
|
||||
}
|
||||
}
|
||||
assertTrue("The reference must contain opaque title glyphs", compared >= 24)
|
||||
} finally { bitmap.recycle() }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
package org.justinvisser.wolfskin.design
|
||||
|
||||
import android.app.Application
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import com.github.takahirom.roborazzi.captureRoboImage
|
||||
import java.io.File
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -64,23 +69,23 @@ class CardGeometryTest {
|
|||
val first = rule.onNodeWithTag("poster-0").fetchSemanticsNode().boundsInRoot
|
||||
val second = rule.onNodeWithTag("poster-6").fetchSemanticsNode().boundsInRoot
|
||||
// Focus scales around the unchanged slot centre.
|
||||
assertEquals(372f, first.top + (first.height - 384f) / 2, 1f)
|
||||
assertEquals(524f, second.top - (first.top + (first.height - 384f) / 2), 1f)
|
||||
assertEquals(372f, first.top + (first.height - 384f) / 2, .1f)
|
||||
assertEquals(524f, second.top - (first.top + (first.height - 384f) / 2), .1f)
|
||||
for (index in 0..5) {
|
||||
val caption = rule.onNodeWithText(titles[index].title, useUnmergedTree = true).fetchSemanticsNode()
|
||||
assertEquals(256f, caption.boundsInRoot.width, .1f)
|
||||
assertEquals(794f, caption.boundsInRoot.top, 1f)
|
||||
assertEquals(794f, caption.boundsInRoot.top, .1f)
|
||||
}
|
||||
val focusedCaption = rule.onNodeWithText(titles[0].title, useUnmergedTree = true).fetchSemanticsNode().boundsInRoot
|
||||
assertEquals(20.48f, focusedCaption.top - first.bottom - first.height * .04f - 2f * 1.08f, 1f)
|
||||
assertEquals(26.48f, second.top - first.height * .04f - 2f * 1.08f - (focusedCaption.top + 58f), 1f)
|
||||
assertEquals(20.48f, focusedCaption.top - first.bottom - first.height * .04f - 2f * 1.08f, .1f)
|
||||
assertEquals(26.48f, second.top - first.height * .04f - 2f * 1.08f - (focusedCaption.top + 58f), .1f)
|
||||
rule.runOnIdle { kotlinx.coroutines.runBlocking { shell.view.listState.scrollToItem(0, 204) } }
|
||||
rule.waitForIdle()
|
||||
val pinnedFirst = rule.onNodeWithTag("poster-0").fetchSemanticsNode().boundsInRoot
|
||||
val pinnedSecond = rule.onNodeWithTag("poster-6").fetchSemanticsNode().boundsInRoot
|
||||
assertEquals(168f, pinnedFirst.top, 1f)
|
||||
assertEquals(692f, pinnedSecond.top, 1f)
|
||||
assertEquals(1076f, pinnedSecond.bottom, 1f)
|
||||
assertEquals(168f, pinnedFirst.top, .1f)
|
||||
assertEquals(692f, pinnedSecond.top, .1f)
|
||||
assertEquals(1076f, pinnedSecond.bottom, .1f)
|
||||
}
|
||||
|
||||
@Test fun homePosterWideAndTileShelvesReserveTheirCaptionBands() {
|
||||
|
|
@ -128,4 +133,37 @@ class CardGeometryTest {
|
|||
rule.onNodeWithContentDescription("Watched").assertDoesNotExist()
|
||||
rule.onNodeWithText("A filmography credit").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test fun loadingCaptionBandsReserveSpaceWithoutPaintingBars() {
|
||||
val slots = listOf("poster" to Triple(128.dp, 192.dp, 48.dp),
|
||||
"wide" to Triple(272.dp, 153.dp, 32.dp), "tile" to Triple(200.dp, 112.5.dp, 32.dp))
|
||||
rule.setContent { WolfskinTheme {
|
||||
Box(Modifier.requiredSize(960.dp, 540.dp).background(WolfskinTokens.Color.bg)) {
|
||||
Row(Modifier.offset(48.dp, 42.dp), horizontalArrangement = Arrangement.spacedBy(20.dp)) {
|
||||
slots.forEach { (name, geometry) ->
|
||||
Box(Modifier.testTag("loading-$name")) {
|
||||
PosterCaptionSkeleton(geometry.first, geometry.second, geometry.third)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} }
|
||||
rule.waitForIdle()
|
||||
val directory = File(System.getProperty("wolfskin.captureDirectory"), "caption-followup").apply { mkdirs() }
|
||||
val file = File(directory, "loading-bands.png")
|
||||
rule.onRoot().captureRoboImage(file.absolutePath)
|
||||
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
|
||||
try {
|
||||
for ((name, geometry) in slots) {
|
||||
val bounds = rule.onNodeWithTag("loading-$name").fetchSemanticsNode().boundsInRoot
|
||||
val band = geometry.third.value * 2
|
||||
assertEquals((geometry.second.value + geometry.third.value) * 2, bounds.height, .1f)
|
||||
for (y in (bounds.bottom - band).toInt() until bounds.bottom.toInt()) {
|
||||
for (x in bounds.left.toInt() until bounds.right.toInt()) {
|
||||
assertEquals("$name reserved caption band ($x,$y)", WolfskinTokens.Color.bg.toArgb(), bitmap.getPixel(x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally { bitmap.recycle() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1224,15 +1224,13 @@
|
|||
"w": 400,
|
||||
"h": 225,
|
||||
"gap": 40,
|
||||
"pitch": 440,
|
||||
"captionBand": 64
|
||||
"pitch": 440
|
||||
},
|
||||
"dp": {
|
||||
"w": 200.0,
|
||||
"h": 112.5,
|
||||
"gap": 20.0,
|
||||
"pitch": 220.0,
|
||||
"captionBand": 32.0
|
||||
"pitch": 220.0
|
||||
},
|
||||
"ratio": "16:9",
|
||||
"fullSlotsPerRow": 4,
|
||||
|
|
@ -1245,15 +1243,13 @@
|
|||
"w": 544,
|
||||
"h": 306,
|
||||
"gap": 40,
|
||||
"pitch": 584,
|
||||
"captionBand": 64
|
||||
"pitch": 584
|
||||
},
|
||||
"dp": {
|
||||
"w": 272.0,
|
||||
"h": 153.0,
|
||||
"gap": 20.0,
|
||||
"pitch": 292.0,
|
||||
"captionBand": 32.0
|
||||
"pitch": 292.0
|
||||
},
|
||||
"ratio": "16:9",
|
||||
"fullSlotsPerRow": 3,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ b3f096b692c395534177cc82ac68f5b801870bc7e28f0dd5cd7c17e2103920a3 discover-token
|
|||
c29dc64f3c3ca900c747b3db8b270e25908948bf5f76388d2c627a612fc7c99d generate_tokens.py
|
||||
eef4e9f8c91306362540eda617c9efad4e19ed688f205f6f486625a5918d8105 home-tokens.json
|
||||
192beb887fc34005db99c87ffbcbaf4a057ee2728475fc96224cf89445019b51 shell-tokens.json
|
||||
c04055d064795e5c7d7eef74756fe1386337002e66ef5e6597951a10cd7952dd src/main/java/org/justinvisser/wolfskin/design/WolfskinTokens.kt
|
||||
3e2ad9a57c15c2ed4713abf383807bb567b27b38eddaaa36c00025e078864e12 src/main/java/org/justinvisser/wolfskin/design/WolfskinTokens.kt
|
||||
062597ea50dff9e6987485f9a83d412918dba3e10109524c26d3c2332a2f5b73 title-tokens.json
|
||||
d7d6b17f8258083c879365f01e06729aa0fa12b69b7cd81e1bceabbbe6ab2a7d tokens.css
|
||||
fe84a56f7e570e1ff8c0ed0b347955b7d5421a75719fbbeffe251811ea2e1ae7 tokens.json
|
||||
ba0da02b03047fbc2f29d538d980e87aa237f8ae36545aa4234600243fe14929 tokens.json
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue