@@ -54,6 +54,11 @@ import org.jetbrains.skiko.*
54
54
* Provides a base implementation for integrating a Compose scene with AWT/Swing.
55
55
* It allows setting Compose content by [setContent], this content should be drawn on [component].
56
56
*
57
+ * This bridge contain 2 components that should be added to the view hirarachy:
58
+ * [component] the main visible Swing component, on which Compose will be shown
59
+ * [invisibleComponent] service component used to bypass Swing issues:
60
+ * - for forcing refocus on input methods change
61
+ *
57
62
* Inheritors should call [attachComposeToComponent], so events that came to [component] will be transferred to [ComposeScene]
58
63
*/
59
64
internal abstract class ComposeBridge (
@@ -66,7 +71,10 @@ internal abstract class ComposeBridge(
66
71
mainOwnerProvider = { scene.mainOwner }
67
72
)
68
73
74
+ private val _invisibleComponent = InvisibleComponent ()
75
+
69
76
abstract val component: JComponent
77
+ val invisibleComponent: Component get() = _invisibleComponent
70
78
71
79
abstract val renderApi: GraphicsApi
72
80
@@ -86,16 +94,30 @@ internal abstract class ComposeBridge(
86
94
87
95
private var window: Window ? = null
88
96
97
+ private fun refocus () {
98
+ if (component.isFocusOwner) {
99
+ _invisibleComponent .requestFocusTemporary()
100
+ component.requestFocus()
101
+ }
102
+ }
103
+
89
104
private val platformComponent: PlatformComponent = object : PlatformComponent {
90
105
override fun enableInput (inputMethodRequests : InputMethodRequests ) {
91
106
currentInputMethodRequests = inputMethodRequests
92
107
component.enableInputMethods(true )
93
- val focusGainedEvent = FocusEvent (focusComponentDelegate, FocusEvent .FOCUS_GAINED )
94
- component.inputContext.dispatchEvent(focusGainedEvent)
108
+ // Without resetting the focus, Swing won't update the status (doesn't show/hide popup)
109
+ // enableInputMethods is design to used per-Swing component level at init stage,
110
+ // not dynamically
111
+ refocus()
95
112
}
96
113
97
114
override fun disableInput () {
98
115
currentInputMethodRequests = null
116
+ component.enableInputMethods(false )
117
+ // Without resetting the focus, Swing won't update the status (doesn't show/hide popup)
118
+ // enableInputMethods is design to used per-Swing component level at init stage,
119
+ // not dynamically
120
+ refocus()
99
121
}
100
122
101
123
override val locationOnScreen: Point
@@ -181,6 +203,7 @@ internal abstract class ComposeBridge(
181
203
182
204
@OptIn(ExperimentalComposeUiApi ::class )
183
205
protected fun attachComposeToComponent () {
206
+ component.enableInputMethods(false )
184
207
component.addInputMethodListener(object : InputMethodListener {
185
208
override fun caretPositionChanged (event : InputMethodEvent ? ) {
186
209
if (isDisposed) return
@@ -393,6 +416,12 @@ internal abstract class ComposeBridge(
393
416
override val touchSlop: Float get() = with (platformComponent.density) { 18 .dp.toPx() }
394
417
}
395
418
}
419
+
420
+ private class InvisibleComponent : Component () {
421
+ fun requestFocusTemporary (): Boolean {
422
+ return super .requestFocus(true )
423
+ }
424
+ }
396
425
}
397
426
398
427
private fun ComposeScene.onMouseEvent (
0 commit comments