All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.peanuuutz.fork.ui.preset.TooltipArea.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 The Android Open Source Project
 * Modifications Copyright 2022 Peanuuutz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.peanuuutz.fork.ui.preset

import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import net.peanuuutz.fork.ui.foundation.draw.BorderStroke
import net.peanuuutz.fork.ui.foundation.draw.background
import net.peanuuutz.fork.ui.foundation.draw.border
import net.peanuuutz.fork.ui.foundation.layout.BasicTooltipArea
import net.peanuuutz.fork.ui.foundation.layout.Box
import net.peanuuutz.fork.ui.foundation.layout.PaddingValues
import net.peanuuutz.fork.ui.foundation.layout.padding
import net.peanuuutz.fork.ui.foundation.layout.popup.rememberPointerPopupPositioner
import net.peanuuutz.fork.ui.foundation.layout.sizeIn
import net.peanuuutz.fork.ui.preset.theme.Theme
import net.peanuuutz.fork.ui.ui.context.LocalWindowInfo
import net.peanuuutz.fork.ui.ui.context.UIContext
import net.peanuuutz.fork.ui.ui.draw.Painter
import net.peanuuutz.fork.ui.ui.layout.Constraints.Companion.Unlimited
import net.peanuuutz.fork.ui.ui.layout.EmptyContentBlock
import net.peanuuutz.fork.ui.ui.layout.PopupPositioner
import net.peanuuutz.fork.ui.ui.modifier.Modifier
import net.peanuuutz.fork.ui.ui.unit.IntSize
import net.peanuuutz.fork.util.common.Color

@Composable
fun TooltipArea(
    tooltip: (@Composable () -> Unit)?,
    context: UIContext,
    modifier: Modifier = Modifier,
    tooltipAreaStyle: TooltipAreaStyle = Theme.tooltipArea,
    tooltipPadding: PaddingValues = TooltipAreaDefaults.TooltipAreaPadding,
    positioner: PopupPositioner = rememberPointerPopupPositioner(),
    content: @Composable () -> Unit
) {
    val resolvedTooltip: @Composable () -> Unit = if (tooltip != null) {
        remember(tooltip, tooltipAreaStyle, tooltipPadding) {
            @Composable {
                val border by tooltipAreaStyle.border()
                val background by tooltipAreaStyle.background()
                val windowHeight = LocalWindowInfo.current?.size?.height ?: Unlimited

                Box(
                    modifier = Modifier
                        .sizeIn(
                            minWidth = TooltipAreaDefaults.TooltipAreaMinSize.width,
                            maxWidth = TooltipAreaDefaults.TooltipAreaMaxWidth,
                            minHeight = TooltipAreaDefaults.TooltipAreaMinSize.height,
                            maxHeight = windowHeight
                        )
                        .border(border)
                        .background(background)
                        .padding(tooltipPadding)
                ) {
                    val contentColor by tooltipAreaStyle.content()

                    CompositionLocalProvider(
                        LocalContentColor provides contentColor,
                        content = tooltip
                    )
                }
            }
        }
    } else {
        EmptyContentBlock
    }

    BasicTooltipArea(
        tooltip = resolvedTooltip,
        context = context,
        modifier = modifier,
        positioner = positioner,
        content = content
    )
}

object TooltipAreaDefaults {
    val TooltipAreaMinSize: IntSize = IntSize(4, 14)

    const val TooltipAreaMaxWidth: Int = 200

    val TooltipAreaPadding: PaddingValues = PaddingValues(2)
}

@Stable
interface TooltipAreaStyle {
    @Composable
    fun border(): State

    @Composable
    fun background(): State

    @Composable
    fun content(): State

    @Stable
    abstract class Delegated(
        val delegate: TooltipAreaStyle
    ) : TooltipAreaStyle by delegate
}

val Theme.tooltipArea: TooltipAreaStyle
    @ReadOnlyComposable
    @Composable
    get() = LocalTooltipArea.current

@NonRestartableComposable
@Composable
fun TooltipAreaStyleProvider(
    tooltipAreaStyle: TooltipAreaStyle,
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(
        LocalTooltipArea provides tooltipAreaStyle,
        content = content
    )
}

@Stable
class DefaultTooltipAreaStyle(
    val border: BorderStroke,
    val background: Painter,
    val content: Color
) : TooltipAreaStyle {
    @Composable
    override fun border(): State {
        return rememberUpdatedState(border)
    }

    @Composable
    override fun background(): State {
        return rememberUpdatedState(background)
    }

    @Composable
    override fun content(): State {
        return rememberUpdatedState(content)
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is DefaultTooltipAreaStyle) return false
        if (border != other.border) return false
        if (background != other.background) return false
        if (content != other.content) return false
        return true
    }

    override fun hashCode(): Int {
        var result = border.hashCode()
        result = 31 * result + background.hashCode()
        result = 31 * result + content.hashCode()
        return result
    }

    override fun toString(): String {
        return "DefaultTooltipAreaStyle(border=$border, background=$background, content=$content)"
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy