How can I hide the scrollbar automatically, seems ...
# compose-desktop
h
How can I hide the scrollbar automatically, seems no config now. Or should i use LazyScroll?
i
You mean hide scrollbar when it is not under the mouse cursor? You can wrap you entire app into this Composable:
Copy code
@Composable
private fun AppTheme(content: @Composable () -> Unit) {
    DesktopMaterialTheme {
        CompositionLocalProvider(
            ScrollbarStyleAmbient provides ScrollbarStyleAmbient.current.copy(
                unhoverColor = Color.Transparent
            ),
            content = content
        )
    }
}
h
Yes, thank you. I'll try it later
@Igor Demin another thing, the code help me hide scrollbar, but how can it show while scrolling, and hide while not scrolling
i
I think it is not possible right now 😅 1. You can write a custom scrollbar:
Copy code
@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).
h
Thanks for response, I'll try the first way. I'll response if have some update
@Igor Demin I finally use the
Modifier.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
Copy code
@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()
        }
    }
}
i
a time-updatable scheduler instead of 
delay
 every time, I think it will have a greater performance because don't have to block event.
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.
h
Sure, but when I test it, the previous coroutine is not canceled or restarted, and
scrolling = false
triggered many times