Hyia
04/10/2021, 3:23 AMIgor Demin
04/10/2021, 8:34 PM@Composable
private fun AppTheme(content: @Composable () -> Unit) {
DesktopMaterialTheme {
CompositionLocalProvider(
ScrollbarStyleAmbient provides ScrollbarStyleAmbient.current.copy(
unhoverColor = Color.Transparent
),
content = content
)
}
}
Hyia
04/12/2021, 12:15 AMHyia
04/12/2021, 1:50 AMIgor Demin
04/13/2021, 9:15 AM@Composable
private fun AppVerticalScrollbar(
adapter: ScrollbarAdapter,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
var isScrollRecently by remember { mutableStateOf(false) }
var withAnimation by remember { mutableStateOf(true) }
val state = remember {
object {
var isFirstComposition = true
}
}
LaunchedEffect(adapter.scrollOffset) {
if (!state.isFirstComposition) {
isScrollRecently = true
withAnimation = false
delay(500)
isScrollRecently = false
}
}
val style = ScrollbarStyleAmbient.current
VerticalScrollbar(
adapter,
modifier,
style.copy(
hoverDurationMillis = if (withAnimation) style.hoverDurationMillis else 0,
unhoverColor = if (isScrollRecently) style.unhoverColor else Color.Transparent
),
interactionSource
)
SideEffect {
withAnimation = true
state.isFirstComposition = false
}
}
But it will not work, probably because of these bugs:
https://github.com/JetBrains/compose-jb/issues/550
https://github.com/JetBrains/compose-jb/issues/549
2. We can add unhoverScrollColor
to ScrollbarStyle
(or something that will help to tune the visual style more).Hyia
04/13/2021, 9:18 AMHyia
04/14/2021, 2:18 AMModifier.mouseScrollFilter
to add a callback and refresh the hide time, and created a time-updatable scheduler instead of delay
every time, I think it will have a greater performance because don't have to block event. and
@Suppress("UNCHECKED_CAST")
class UpdatableScheduler(private val pollSize: Int = 1) {
private val queue = DelayQueue<UpdatableTask>()
private val executor = ThreadPoolExecutor(
0, 1, 0, TimeUnit.MILLISECONDS,
queue as BlockingQueue<Runnable>
)
fun schedule(delay: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS, block: () -> Unit): UpdatableTask {
return schedule(UpdatableTask(delay, timeUnit, block))
}
fun schedule(task: UpdatableTask): UpdatableTask {
if (!queue.contains(task)) {
executor.execute(task)
}
return task
}
inner class UpdatableTask(
private var delay: Long, private val timeUnit: TimeUnit = TimeUnit.MILLISECONDS,
val block: () -> Unit
) : Delayed, Runnable {
@Volatile
private var init = System.currentTimeMillis()
fun reset() {
this@UpdatableScheduler.queue.remove(this)
init = System.currentTimeMillis()
this@UpdatableScheduler.schedule(this)
}
override fun compareTo(other: Delayed): Int {
return delay.compareTo(other.getDelay(timeUnit))
}
override fun getDelay(unit: TimeUnit): Long {
val remain = init + TimeUnit.MILLISECONDS.convert(delay, timeUnit) - System.currentTimeMillis()
return unit.convert(remain, TimeUnit.MILLISECONDS)
}
override fun run() {
block()
}
}
}
Igor Demin
04/14/2021, 6:50 AMa time-updatable scheduler instead ofevery time, I think it will have a greater performance because don't have to block event.delay
delay
doesn't block the thread, it only suspends coroutine.
LaunchedEffect
restarts coroutine every time adapter.scrollOffset
changes.
When coroutine is restarted or cancelled, delay
ends immediately.Hyia
04/14/2021, 6:55 AMscrolling = false
triggered many times