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
d599c03b
Unverified
Commit
d599c03b
authored
May 09, 2024
by
Alexander Maryanovsky
Committed by
GitHub
May 09, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support accessibility in SkiaSwingLayer (#920)
parent
57dea506
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
88 additions
and
45 deletions
+88
-45
Accessibility.kt
...o/src/awtMain/kotlin/org/jetbrains/skiko/Accessibility.kt
+62
-1
HardwareLayer.kt
...o/src/awtMain/kotlin/org/jetbrains/skiko/HardwareLayer.kt
+10
-42
SkiaSwingLayer.kt
...wtMain/kotlin/org/jetbrains/skiko/swing/SkiaSwingLayer.kt
+16
-2
No files found.
skiko/src/awtMain/kotlin/org/jetbrains/skiko/Accessibility.kt
View file @
d599c03b
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
*/
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
skiko/src/awtMain/kotlin/org/jetbrains/skiko/HardwareLayer.kt
View file @
d599c03b
...
...
@@ -6,10 +6,7 @@ import org.jetbrains.skiko.redrawer.dispatcherToBlockOn
import
java.awt.Canvas
import
java.awt.Component
import
java.awt.Graphics
import
java.awt.KeyboardFocusManager
import
java.awt.event.FocusEvent
import
java.awt.event.InputMethodEvent
import
java.beans.PropertyChangeEvent
import
javax.accessibility.Accessible
import
javax.accessibility.AccessibleContext
...
...
@@ -29,7 +26,7 @@ internal open class HardwareLayer(
}
open
fun
dispose
()
{
resetFocusAccessibleJob
?.
cancel
()
nativeAccessibleFocusHelper
.
dispose
()
nativeDispose
()
}
...
...
@@ -61,47 +58,18 @@ internal open class HardwareLayer(
private
external
fun
getWindowHandle
(
platformInfo
:
Long
):
Long
private
external
fun
getCurrentDPI
(
platformInfo
:
Long
):
Int
private
val
_externalAccessible
=
externalAccessibleFactory
?.
invoke
(
this
)
private
var
_focusedAccessible
:
Accessible
?
=
null
@Suppress
(
"LeakingThis"
)
private
val
nativeAccessibleFocusHelper
=
NativeAccessibleFocusHelper
(
component
=
this
,
externalAccessible
=
externalAccessibleFactory
?.
invoke
(
this
)
)
override
fun
getAccessibleContext
():
AccessibleContext
{
val
res
=
(
_focusedAccessible
?:
_externalAccessible
)
?.
accessibleContext
return
res
?:
super
.
getAccessibleContext
()
return
nativeAccessibleFocusHelper
.
accessibleContext
?:
super
.
getAccessibleContext
()
}
private
var
resetFocusAccessibleJob
:
Job
?
=
null
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
(
this
,
FocusEvent
.
FOCUS_GAINED
)
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
)
}
nativeAccessibleFocusHelper
.
requestNativeFocusOnAccessible
(
accessible
)
}
}
...
...
@@ -144,7 +112,7 @@ internal fun layerFrameLimiter(
*
* 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 [requestNativeFocusOnAccessible], wrong mapping can be built
* 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,
...
...
skiko/src/awtMain/kotlin/org/jetbrains/skiko/swing/SkiaSwingLayer.kt
View file @
d599c03b
...
...
@@ -3,10 +3,13 @@ package org.jetbrains.skiko.swing
import
org.jetbrains.skia.Canvas
import
org.jetbrains.skiko.*
import
org.jetbrains.skiko.redrawer.RedrawerManager
import
java.awt.Component
import
java.awt.Graphics2D
import
java.awt.GraphicsConfiguration
import
javax.accessibility.Accessible
import
javax.accessibility.AccessibleContext
import
javax.swing.JComponent
import
javax.swing.JPanel
import
javax.swing.SwingUtilities.isEventDispatchThread
/**
...
...
@@ -24,7 +27,8 @@ import javax.swing.SwingUtilities.isEventDispatchThread
open
class
SkiaSwingLayer
(
renderDelegate
:
SkikoRenderDelegate
,
analytics
:
SkiaLayerAnalytics
=
SkiaLayerAnalytics
.
Empty
,
)
:
JComponent
()
{
externalAccessibleFactory
:
((
Component
)
->
Accessible
)?
=
null
,
)
:
JPanel
()
{
internal
companion
object
{
init
{
Library
.
load
()
...
...
@@ -118,7 +122,17 @@ 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
?)
{
// TODO: support accessibility
nativeAccessibleFocusHelper
.
requestNativeFocusOnAccessible
(
accessible
)
}
}
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