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