Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
skiko
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
liuqiang
skiko
Commits
d6e53c50
Unverified
Commit
d6e53c50
authored
Jan 19, 2026
by
Alexander Maryanovsky
Committed by
GitHub
Jan 19, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove `NativeAccessibleFocusHelper` (#1147)
parent
3799325e
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
21 additions
and
126 deletions
+21
-126
Accessibility.kt
...o/src/awtMain/kotlin/org/jetbrains/skiko/Accessibility.kt
+11
-63
HardwareLayer.kt
...o/src/awtMain/kotlin/org/jetbrains/skiko/HardwareLayer.kt
+2
-40
SkiaLayer.awt.kt
...o/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt
+6
-10
SkiaSwingLayer.kt
...wtMain/kotlin/org/jetbrains/skiko/swing/SkiaSwingLayer.kt
+2
-13
No files found.
skiko/src/awtMain/kotlin/org/jetbrains/skiko/Accessibility.kt
View file @
d6e53c50
package
org.jetbrains.skiko
import
kotlinx.coroutines.*
import
java.awt.Component
import
java.awt.KeyboardFocusManager
import
java.awt.event.FocusEvent
import
java.beans.PropertyChangeEvent
import
javax.accessibility.Accessible
import
javax.accessibility.AccessibleContext
/**
* See [nativeInitializeAccessible] doc for details
* Calls `sun.lwawt.macosx.CAccessible.getCAccessible(Accessible)` on the given [accessible] object; does nothing
* if running on a non-Mac platform or in a JVM where there is no `CAccessible` class.
*
* Ideally, this and nativeInitializeAccessible should be in Compose, not Skiko.
* Unfortunately, Compose doesn't currently allow native code, and implementing it via reflection is not
* possible due to java.desktop module access restrictions:
* > class androidx.compose.ui.platform.a11y.AccessibilityKt cannot access class sun.lwawt.macosx.CAccessible
* > (in module java.desktop) because module java.desktop does not export sun.lwawt.macosx to unnamed module
*
* As such, this function is not to be considered public Skiko API.
*/
internal
external
fun
initializeCAccessible
(
accessible
:
Accessible
)
/**
* A helper class for implementing requesting accessibility focus on a given accessible.
*/
internal
class
NativeAccessibleFocusHelper
(
private
val
component
:
Component
,
private
val
externalAccessible
:
Accessible
?,
)
{
private
var
focusedAccessible
:
Accessible
?
=
null
val
accessibleContext
:
AccessibleContext
?
get
()
=
(
focusedAccessible
?:
externalAccessible
)
?.
accessibleContext
private
var
resetFocusAccessibleJob
:
Job
?
=
null
@OptIn
(
DelicateCoroutinesApi
::
class
)
fun
requestNativeFocusOnAccessible
(
accessible
:
Accessible
?)
{
focusedAccessible
=
accessible
when
(
hostOs
)
{
OS
.
Windows
->
requestAccessBridgeFocusOnAccessible
()
OS
.
MacOS
->
requestMacOSFocusOnAccessible
(
accessible
)
else
->
{
focusedAccessible
=
null
return
}
}
// Listener spawns asynchronous notification post procedure, reading current focus owner
// and its accessibility context. This timeout is used to deal with concurrency
// TODO Find more reliable procedure
resetFocusAccessibleJob
?.
cancel
()
resetFocusAccessibleJob
=
GlobalScope
.
launch
(
MainUIDispatcher
)
{
delay
(
100
)
focusedAccessible
=
null
}
}
private
fun
requestAccessBridgeFocusOnAccessible
()
{
val
focusEvent
=
FocusEvent
(
component
,
FocusEvent
.
FOCUS_GAINED
)
component
.
focusListeners
.
forEach
{
it
.
focusGained
(
focusEvent
)
}
}
private
fun
requestMacOSFocusOnAccessible
(
accessible
:
Accessible
?)
{
val
focusManager
=
KeyboardFocusManager
.
getCurrentKeyboardFocusManager
()
val
listeners
=
focusManager
.
getPropertyChangeListeners
(
"focusOwner"
)
val
event
=
PropertyChangeEvent
(
focusManager
,
"focusOwner"
,
null
,
accessible
)
listeners
.
forEach
{
it
.
propertyChange
(
event
)
}
}
fun
dispose
()
{
resetFocusAccessibleJob
?.
cancel
()
}
}
\ No newline at end of file
external
fun
initializeCAccessible
(
accessible
:
Accessible
)
skiko/src/awtMain/kotlin/org/jetbrains/skiko/HardwareLayer.kt
View file @
d6e53c50
...
...
@@ -7,11 +7,10 @@ import java.awt.Canvas
import
java.awt.Component
import
java.awt.Graphics
import
java.awt.event.InputMethodEvent
import
javax.accessibility.Accessible
import
javax.accessibility.AccessibleContext
internal
open
class
HardwareLayer
(
externalAccessibleFactory
:
((
Component
)
->
Accessible
)?
=
null
private
val
accessibleContextProvider
:
((
Component
)
->
AccessibleContext
)?
=
null
)
:
Canvas
()
{
companion
object
{
init
{
...
...
@@ -26,7 +25,6 @@ internal open class HardwareLayer(
}
open
fun
dispose
()
{
nativeAccessibleFocusHelper
.
dispose
()
nativeDispose
()
}
...
...
@@ -58,18 +56,8 @@ internal open class HardwareLayer(
private
external
fun
getWindowHandle
(
platformInfo
:
Long
):
Long
private
external
fun
getCurrentDPI
(
platformInfo
:
Long
):
Int
@Suppress
(
"LeakingThis"
)
private
val
nativeAccessibleFocusHelper
=
NativeAccessibleFocusHelper
(
component
=
this
,
externalAccessible
=
externalAccessibleFactory
?.
invoke
(
this
)
)
override
fun
getAccessibleContext
():
AccessibleContext
{
return
nativeAccessibleFocusHelper
.
accessibleContext
?:
super
.
getAccessibleContext
()
}
fun
requestNativeFocusOnAccessible
(
accessible
:
Accessible
?)
{
nativeAccessibleFocusHelper
.
requestNativeFocusOnAccessible
(
accessible
)
return
accessibleContextProvider
?.
invoke
(
this
)
?:
super
.
getAccessibleContext
()
}
}
...
...
@@ -106,29 +94,3 @@ internal fun layerFrameLimiter(
}
)
}
/**
* This method should be called on custom [Accessible] creation (or its context if context is created lazily).
*
* JDK's accessibility support (at least for MacOS) builds mapping AccessibleContext -> Accessible.
* Some [Accessible] are built only when focus is settled and
* since we have a hack [NativeAccessibleFocusHelper.requestNativeFocusOnAccessible], wrong mapping can be built
* (ComponentAccessibleContext -> SkiaLayer instead of ComponentAccessibleContext -> ComponentAccessible).
*
* This method forces JDK's accessibility support to cache mapping ComponentAccessibleContext -> ComponentAccessible,
* if it is called on ComponentAccessibleContext creation.
*
* Related to the [issue](https://youtrack.jetbrains.com/issue/COMPOSE-176).
*/
@Suppress
(
"unused"
)
fun
nativeInitializeAccessible
(
accessible
:
Accessible
)
{
when
(
hostOs
)
{
OS
.
MacOS
->
{
initializeCAccessible
(
accessible
)
}
else
->
{
// TODO: do we need something for Windows?
}
}
}
\ No newline at end of file
skiko/src/awtMain/kotlin/org/jetbrains/skiko/SkiaLayer.awt.kt
View file @
d6e53c50
...
...
@@ -27,7 +27,7 @@ import javax.swing.event.AncestorListener
import
kotlin.math.floor
actual
open
class
SkiaLayer
internal
constructor
(
externalAccessibleFactory
:
((
Component
)
->
Accessible
)?
=
null
,
accessibleContextProvider
:
((
Component
)
->
AccessibleContext
)?
=
null
,
val
properties
:
SkiaLayerProperties
,
private
val
renderFactory
:
RenderFactory
=
RenderFactory
.
Default
,
private
val
analytics
:
SkiaLayerAnalytics
=
SkiaLayerAnalytics
.
Empty
,
...
...
@@ -48,7 +48,7 @@ actual open class SkiaLayer internal constructor(
internal
val
backedLayer
:
HardwareLayer
constructor
(
externalAccessibleFactory
:
((
Component
)
->
Accessible
)?
=
null
,
accessibleContextProvider
:
((
Component
)
->
AccessibleContext
)?
=
null
,
isVsyncEnabled
:
Boolean
=
SkikoProperties
.
vsyncEnabled
,
isVsyncFramelimitFallbackEnabled
:
Boolean
=
SkikoProperties
.
vsyncFramelimitFallbackEnabled
,
frameBuffering
:
FrameBuffering
=
SkikoProperties
.
frameBuffering
,
...
...
@@ -56,7 +56,7 @@ actual open class SkiaLayer internal constructor(
analytics
:
SkiaLayerAnalytics
=
SkiaLayerAnalytics
.
Empty
,
pixelGeometry
:
PixelGeometry
=
PixelGeometry
.
UNKNOWN
,
)
:
this
(
externalAccessibleFactory
,
accessibleContextProvider
,
SkiaLayerProperties
(
isVsyncEnabled
,
isVsyncFramelimitFallbackEnabled
,
...
...
@@ -69,12 +69,12 @@ actual open class SkiaLayer internal constructor(
)
constructor
(
externalAccessibleFactory
:
((
Component
)
->
Accessible
)?
=
null
,
accessibleContextProvider
:
((
Component
)
->
AccessibleContext
)?
=
null
,
properties
:
SkiaLayerProperties
,
analytics
:
SkiaLayerAnalytics
=
SkiaLayerAnalytics
.
Empty
,
pixelGeometry
:
PixelGeometry
=
PixelGeometry
.
UNKNOWN
,
)
:
this
(
externalAccessibleFactory
,
accessibleContextProvider
,
properties
,
RenderFactory
.
Default
,
analytics
,
...
...
@@ -89,7 +89,7 @@ actual open class SkiaLayer internal constructor(
init
{
layout
=
null
backedLayer
=
object
:
HardwareLayer
(
externalAccessibleFactory
)
{
backedLayer
=
object
:
HardwareLayer
(
accessibleContextProvider
)
{
override
fun
paint
(
g
:
Graphics
)
{
Logger
.
debug
{
"Paint called on HardwareLayer $this"
}
checkContentScale
()
...
...
@@ -683,10 +683,6 @@ actual open class SkiaLayer internal constructor(
}
}
fun
requestNativeFocusOnAccessible
(
accessible
:
Accessible
?)
{
backedLayer
.
requestNativeFocusOnAccessible
(
accessible
)
}
override
fun
getAccessibleContext
():
AccessibleContext
{
if
(
accessibleContext
==
null
)
{
accessibleContext
=
AccessibleSkiaLayer
()
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/swing/SkiaSwingLayer.kt
View file @
d6e53c50
...
...
@@ -7,7 +7,6 @@ import java.awt.Component
import
java.awt.Graphics
import
java.awt.Graphics2D
import
java.awt.GraphicsConfiguration
import
javax.accessibility.Accessible
import
javax.accessibility.AccessibleContext
import
javax.swing.JPanel
import
javax.swing.SwingUtilities.isEventDispatchThread
...
...
@@ -27,7 +26,7 @@ import javax.swing.SwingUtilities.isEventDispatchThread
open
class
SkiaSwingLayer
(
renderDelegate
:
SkikoRenderDelegate
,
analytics
:
SkiaLayerAnalytics
=
SkiaLayerAnalytics
.
Empty
,
externalAccessibleFactory
:
((
Component
)
->
Accessible
)?
=
null
,
private
val
accessibleContextProvider
:
((
Component
)
->
AccessibleContext
)?
=
null
,
private
val
properties
:
SkiaLayerProperties
=
SkiaLayerProperties
()
)
:
JPanel
()
{
internal
companion
object
{
...
...
@@ -126,17 +125,7 @@ open class SkiaSwingLayer(
}
}
@Suppress
(
"LeakingThis"
)
private
val
nativeAccessibleFocusHelper
=
NativeAccessibleFocusHelper
(
component
=
this
,
externalAccessible
=
externalAccessibleFactory
?.
invoke
(
this
)
)
override
fun
getAccessibleContext
():
AccessibleContext
?
{
return
nativeAccessibleFocusHelper
.
accessibleContext
?:
super
.
getAccessibleContext
()
}
fun
requestNativeFocusOnAccessible
(
accessible
:
Accessible
?)
{
nativeAccessibleFocusHelper
.
requestNativeFocusOnAccessible
(
accessible
)
return
accessibleContextProvider
?.
invoke
(
this
)
?:
super
.
getAccessibleContext
()
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment