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

commonMain.io.nacular.doodle.accessibility.AccessibilityManager.kt Maven / Gradle / Ivy

package io.nacular.doodle.accessibility

import io.nacular.doodle.core.Internal
import io.nacular.doodle.core.View
import io.nacular.doodle.utils.Orientation
import io.nacular.doodle.utils.observable
import kotlin.properties.ReadWriteProperty

/**
 * Manages all accessibility interactions within an application.
 */
public interface AccessibilityManager {
    @Internal public fun syncLabel        (view: View)
    @Internal public fun syncEnabled      (view: View)
    @Internal public fun syncVisibility   (view: View)
    @Internal public fun syncDescription  (view: View)
    @Internal public fun syncNextReadOrder(view: View)
    @Internal public fun roleAdopted      (view: View)
    @Internal public fun roleUpdated      (view: View)
    @Internal public fun roleAbandoned    (view: View)

    @Internal public fun addOwnership   (owner: View, owned: View)
    @Internal public fun removeOwnership(owner: View, owned: View)
}

/**
 * Base class for all accessible roles a [View] can take.
 * @see [View.accessibilityRole]
 */
public sealed class AccessibilityRole {
    @Internal public      var manager: AccessibilityManager? = null
    @Internal public      var view   : View?                 = null
    @Internal public open val name   : String?               = when {
        this::class.simpleName?.lowercase()?.endsWith("role") == true -> this::class.simpleName?.toLowerCase()?.dropLast(4)
        else                                                          -> this::class.simpleName?.toLowerCase()
    }
}

/**
 * Role for items that represent a range with a specified value within it.
 * The properties must be kept in sync with the View with this role.
 */
public open class RangeRole internal constructor(): AccessibilityRole() {
    /** max value for the range */
    public var max: Double by roleProperty(0.0)

    /** min value for the range */
    public var min: Double by roleProperty(0.0)

    /** current value for the range */
    public var value: Double by roleProperty(0.0)

    /** text representation for [value] */
    public var valueText: String? by roleProperty(null)
}

/** Indicates a View that shows progress. */
public class ProgressBarRole: RangeRole()

/** Indicates a View that lets a user slide a value between a max and min value. */
public class SliderRole: RangeRole() {
    /** visual orientation of the slider */
    public var orientation: Orientation? by roleProperty(null)
}

public class ImageRole: AccessibilityRole()

/** Indicates a View that a user can click to take an action. */
public open class ButtonRole internal constructor(): AccessibilityRole() {
    public companion object {
        public operator fun invoke(): ButtonRole = ButtonRole()
    }
}

/** Indicates a button that toggles between selected/deselected when clicked. */
public open class ToggleButtonRole internal constructor(): ButtonRole() {
    override val name: String? get() = super.name

    public var pressed: Boolean by roleProperty(false)

    public companion object {
        public operator fun invoke(): ToggleButtonRole = ToggleButtonRole()
    }
}

/** Indicates a toggle-button that toggles between checked/un-checked when clicked. */
public class CheckBoxRole: ToggleButtonRole()

/** Indicates a toggle-button used (usually within a group) to select a single option from many. */
public class RadioRole: ToggleButtonRole()

/** A toggle-button that generally indicates an on/off option */
public class SwitchRole: ToggleButtonRole()

/** A hyperlink that navigates to a url. */
public class LinkRole: ButtonRole()

/** A container with a sequential set of [ListItemRole]. */
public class ListRole: AccessibilityRole()

/** An item in a [ListRole]. */
public class ListItemRole: AccessibilityRole() {
    /** The index of the item within its list. */
    public var index: Int? by roleProperty(null)

    /** The size of the list this item belongs to. */
    public var listSize: Int? by roleProperty(null)
}

/** A container with a hierarchy of nested [TreeItemRole]. */
public class TreeRole: AccessibilityRole()

/** A node within a [TreeRole] */
public class TreeItemRole: AccessibilityRole() {
    /** The index of the item within its tree. */
    public var index: Int? by roleProperty(null)

    /** The nested leve of the item. */
    public var depth: Int by roleProperty(0)

    /** Size of the tree this item belongs to */
    public var treeSize: Int? by roleProperty(null)

    /** Expanded state of this item (if it has descendants) */
    public var expanded: Boolean by roleProperty(false)
}

/** A View that allows text entry. */
public class TextBoxRole: AccessibilityRole() {
    /** A short hint indicate the purpose of the field. */
    public var placeHolder: String? by roleProperty(null)
}

/** Item used as the interactive tab for a tabbed-panel. */
public class TabRole: AccessibilityRole() {
    public var selected: Boolean by roleProperty(false)
}

/** View that holds the [TabRole]s for a tabbed-panel. */
public class TabListRole: AccessibilityRole() {
    @Internal public val tabToPanelMap: MutableMap = mutableMapOf()

    /** Creates a relationship between a tab's View and the panel it controls */
    public operator fun set(tab: View, tabPanel: View) {
        tabToPanelMap.put(tab, tabPanel)?.also {
            manager?.removeOwnership(tab, it)
            it.accessibilityRole = null
        }

        manager?.addOwnership(tab, tabPanel)
        tabPanel.accessibilityRole = TabPanelRole()
    }

    /** Removes the relationship between a tab's View and the panel it controls */
    public operator fun minusAssign(tab: View) {
        tabToPanelMap.remove(tab)?.also {
            manager?.removeOwnership(tab, it)
            it.accessibilityRole = null
        }
    }
}

/** Displayed item associated with a [TabRole] */
public class TabPanelRole: AccessibilityRole()

internal class spinbutton: RangeRole()
internal class alert: AccessibilityRole()
internal class alertdialog: AccessibilityRole()
internal class dialog: AccessibilityRole()
internal class gridcell: AccessibilityRole()
internal class log: AccessibilityRole()
internal class marquee: AccessibilityRole()
internal class menuitem: AccessibilityRole()
internal class menuitemcheckbox: AccessibilityRole()
internal class menuitemradio: AccessibilityRole()
internal class option: AccessibilityRole()

internal class scrollbar: RangeRole()

internal class status: AccessibilityRole()
internal class timer: AccessibilityRole()
internal class tooltip: AccessibilityRole()

internal class combobox: AccessibilityRole()
internal class grid: AccessibilityRole()
internal class listbox: AccessibilityRole()
internal class menu: AccessibilityRole()
internal class menubar: AccessibilityRole()
internal class radiogroup: AccessibilityRole()
internal class treegrid: AccessibilityRole()

internal class article: AccessibilityRole()
internal class columnheader: AccessibilityRole()
internal class definition: AccessibilityRole()
internal class directory: AccessibilityRole()
internal class document: AccessibilityRole()
internal class group: AccessibilityRole()
internal class heading: AccessibilityRole()
internal class math: AccessibilityRole()
internal class note: AccessibilityRole()
internal class presentation: AccessibilityRole()
internal class region: AccessibilityRole()
internal class row: AccessibilityRole()
internal class rowgroup: AccessibilityRole()
internal class rowheader: AccessibilityRole()
internal class separator: AccessibilityRole()
internal class toolbar: AccessibilityRole()

internal class application: AccessibilityRole()
internal class banner: AccessibilityRole()
internal class complementary: AccessibilityRole()
internal class contentinfo: AccessibilityRole()
internal class form: AccessibilityRole()
internal class main: AccessibilityRole()
internal class navigation: AccessibilityRole()
internal class search: AccessibilityRole()

private inline fun  roleProperty(initial: T, noinline onChange: R.(old: T, new: T) -> Unit = { _,_ -> }): ReadWriteProperty = observable(initial) { old, new ->
    view?.let { manager?.roleUpdated(it) }

    onChange(old, new)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy