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

jvmMain.io.nacular.doodle.theme.native.Utils.kt Maven / Gradle / Ivy

There is a newer version: 0.10.4
Show newest version
package io.nacular.doodle.theme.native

import io.nacular.doodle.drawing.Color
import io.nacular.doodle.drawing.Font
import io.nacular.doodle.drawing.Font.Style
import io.nacular.doodle.drawing.Font.Style.Italic
import io.nacular.doodle.drawing.Font.Style.Normal
import io.nacular.doodle.drawing.Font.Style.Oblique
import io.nacular.doodle.event.PointerEvent
import io.nacular.doodle.geometry.Point
import io.nacular.doodle.system.SystemInputEvent.Modifier.Alt
import io.nacular.doodle.system.SystemInputEvent.Modifier.Ctrl
import io.nacular.doodle.system.SystemInputEvent.Modifier.Meta
import io.nacular.doodle.system.SystemInputEvent.Modifier.Shift
import io.nacular.doodle.system.SystemPointerEvent.Button.Button2
import io.nacular.doodle.system.SystemPointerEvent.Button.Button3
import io.nacular.doodle.system.SystemPointerEvent.Type.Click
import io.nacular.doodle.system.SystemPointerEvent.Type.Down
import io.nacular.doodle.system.SystemPointerEvent.Type.Drag
import io.nacular.doodle.system.SystemPointerEvent.Type.Enter
import io.nacular.doodle.system.SystemPointerEvent.Type.Exit
import io.nacular.doodle.system.SystemPointerEvent.Type.Move
import io.nacular.doodle.system.SystemPointerEvent.Type.Up
import io.nacular.measured.units.Angle
import io.nacular.measured.units.div
import io.nacular.measured.units.normalize
import io.nacular.measured.units.times
import kotlinx.datetime.Clock
import org.jetbrains.skia.FontSlant.ITALIC
import org.jetbrains.skia.FontSlant.OBLIQUE
import org.jetbrains.skia.FontSlant.UPRIGHT
import org.jetbrains.skia.FontStyle
import org.jetbrains.skia.paragraph.BaselineMode.IDEOGRAPHIC
import org.jetbrains.skia.paragraph.TextStyle
import java.awt.Component
import java.awt.event.InputEvent.ALT_DOWN_MASK
import java.awt.event.InputEvent.BUTTON1_DOWN_MASK
import java.awt.event.InputEvent.BUTTON2_DOWN_MASK
import java.awt.event.InputEvent.CTRL_DOWN_MASK
import java.awt.event.InputEvent.META_DOWN_MASK
import java.awt.event.InputEvent.SHIFT_DOWN_MASK
import java.awt.event.MouseEvent
import java.awt.event.MouseEvent.BUTTON1
import java.awt.event.MouseEvent.BUTTON2
import java.awt.event.MouseEvent.BUTTON3
import java.awt.event.MouseEvent.BUTTON3_DOWN_MASK
import java.awt.event.MouseEvent.MOUSE_CLICKED
import java.awt.event.MouseEvent.MOUSE_DRAGGED
import java.awt.event.MouseEvent.MOUSE_ENTERED
import java.awt.event.MouseEvent.MOUSE_EXITED
import java.awt.event.MouseEvent.MOUSE_MOVED
import java.awt.event.MouseEvent.MOUSE_PRESSED
import java.awt.event.MouseEvent.MOUSE_RELEASED
import java.awt.event.MouseWheelEvent
import java.awt.font.TextAttribute.FAMILY
import java.awt.font.TextAttribute.POSTURE
import java.awt.font.TextAttribute.POSTURE_OBLIQUE
import java.awt.font.TextAttribute.POSTURE_REGULAR
import java.awt.font.TextAttribute.SIZE
import java.awt.font.TextAttribute.WEIGHT
import org.jetbrains.skia.Font as SkiaFont
import java.awt.Color as AwtColor
import java.awt.Font as AwtFont

internal fun PointerEvent.toAwt(target: Component, at: Point = location): MouseEvent {
    val id = when (type) {
        Up    -> MOUSE_RELEASED
        Down  -> MOUSE_PRESSED
        Move  -> MOUSE_MOVED
        Exit  -> MOUSE_EXITED
        Drag  -> MOUSE_DRAGGED
        Click -> MOUSE_CLICKED
        Enter -> MOUSE_ENTERED
    }

    val button = when (buttons) {
        setOf(Button2) -> BUTTON2
        setOf(Button3) -> BUTTON3
        else           -> BUTTON1
    }

    // FIXME: Inject
    val time = Clock.System.now().toEpochMilliseconds()

    var modifiers = when (button) {
        BUTTON2 -> BUTTON2_DOWN_MASK
        BUTTON3 -> BUTTON3_DOWN_MASK
        else    -> BUTTON1_DOWN_MASK
    }

    if (Alt in this.modifiers) {
        modifiers = modifiers or ALT_DOWN_MASK
    }

    if (Ctrl in this.modifiers) {
        modifiers = modifiers or CTRL_DOWN_MASK
    }

    if (Shift in this.modifiers) {
        modifiers = modifiers or SHIFT_DOWN_MASK
    }

    if (Meta in this.modifiers) {
        modifiers = modifiers or META_DOWN_MASK
    }

    return MouseEvent(target, id, time, modifiers, at.x.toInt(), at.y.toInt(), clickCount, false, button)
}

internal fun MouseEvent.update(target: Component, location: java.awt.Point): MouseEvent = when (this) {
    is MouseWheelEvent -> MouseWheelEvent(
                        target,
                        id,
                        `when`,
                        modifiersEx,
                        location.x, location.y,
                        clickCount, isPopupTrigger, scrollType, scrollAmount, wheelRotation)
    else -> MouseEvent(target, id, `when`, modifiersEx, location.x, location.y, clickCount, isPopupTrigger)
}

private const val MAX_SKIA_FONT_WEIGHT = 1000f

internal fun AwtFont.skiaStyle(): FontStyle {
    val w = attributes[WEIGHT] as? Float?

    val weight = when {
        w == null || w > 1f -> when (style) {
            AwtFont.PLAIN                 -> FontStyle.NORMAL
            AwtFont.BOLD                  -> FontStyle.BOLD
            AwtFont.ITALIC                -> FontStyle.ITALIC
            AwtFont.BOLD + AwtFont.ITALIC -> FontStyle.BOLD_ITALIC
            else                          -> FontStyle.NORMAL
        }.weight
        else -> (w * MAX_SKIA_FONT_WEIGHT).toInt()
    }

    return FontStyle(weight, size, (attributes[POSTURE] as? Float).let {
        when (it) {
            POSTURE_OBLIQUE -> ITALIC
            else            -> UPRIGHT
        }
    })
}

internal fun SkiaFont.toAwt() = AwtFont(mutableMapOf(
        SIZE    to size,
        FAMILY  to typefaceOrDefault.familyName,
        WEIGHT  to typefaceOrDefault.fontStyle.weight / MAX_SKIA_FONT_WEIGHT,
        POSTURE to typefaceOrDefault.fontStyle.slant.run {
            when (this) {
                ITALIC  -> POSTURE_OBLIQUE
                OBLIQUE -> POSTURE_OBLIQUE
                else    -> POSTURE_REGULAR
            }
        }
))

internal fun Font?.toAwt(default: SkiaFont) = when (this) {
    null -> default.toAwt()
    else -> AwtFont(mutableMapOf(
        SIZE    to size,
        FAMILY  to family,
        WEIGHT  to weight / MAX_SKIA_FONT_WEIGHT,
        POSTURE to style.run {
            when (this) {
                Normal     -> POSTURE_REGULAR
                Italic     -> POSTURE_OBLIQUE
                is Oblique -> angle?.normalize()?.div(360 * Angle.degrees) ?: POSTURE_OBLIQUE
            }
        }
    ))
}

internal fun AwtFont?.toDoodle(): Font? = when (this) {
    null -> null
    else -> object: Font {
        override val size  : Int    get() = [email protected]
        override val style : Style  get() = when {
            [email protected]      -> Italic
            [email protected] -> Oblique()
            else                        -> Normal
        }
        override val weight: Int    get() = [email protected][WEIGHT] as? Int ?: 0
        override val family: String get() = [email protected]
    }
}

internal fun SkiaFont.textStyle() = TextStyle().apply {
    fontSize     = size
    typeface     = typefaceOrDefault
    fontStyle    = typefaceOrDefault.fontStyle
    fontFamilies = typefaceOrDefault.familyNames.map { it.name }.toTypedArray()
    baselineMode = IDEOGRAPHIC
}

internal fun Color.toAwt() = AwtColor(red.toInt(), green.toInt(), blue.toInt(), (opacity * 255).toInt())




© 2015 - 2024 Weber Informatics LLC | Privacy Policy