mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[JEWEL-174] Fixed ToolWindow focus
- The `.trackActivation()` call on the Column modifier was overriding the component activation value - With modifier added, we would only track the activation IF the column (or any descendant) get the focus - To fix the problem, I just removed the track activation to use the ToolWindow activation state - Additionally, I've added a FocusRequester to focus on the item when we open the ToolWindow - This is similar to what is done in the ReleaseDemo - Added click listener to recover focus in the compose window when clicking on it closes https://github.com/JetBrains/intellij-community/pull/3228 GitOrigin-RevId: 554433974bae42126d493290d8a6bef67f9ea135
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d6633fa925
commit
3caa4d60c7
@@ -5,9 +5,13 @@ f:org.jetbrains.jewel.bridge.BridgeUtilsKt
|
||||
- *sf:toNonNegativeDpSize(java.awt.Dimension):J
|
||||
f:org.jetbrains.jewel.bridge.JewelComposePanelWrapperKt
|
||||
- *sf:JewelComposeNoThemePanel(kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
- *sf:JewelComposeNoThemePanel(Z,kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
- *bs:JewelComposeNoThemePanel$default(kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2,I,java.lang.Object):javax.swing.JComponent
|
||||
- *bs:JewelComposeNoThemePanel$default(Z,kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2,I,java.lang.Object):javax.swing.JComponent
|
||||
- *sf:composeWithoutTheme(kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
- *sf:composeWithoutTheme(Z,kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
- *bs:composeWithoutTheme$default(kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2,I,java.lang.Object):javax.swing.JComponent
|
||||
- *bs:composeWithoutTheme$default(Z,kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2,I,java.lang.Object):javax.swing.JComponent
|
||||
- *sf:getLocalComponent():androidx.compose.runtime.ProvidableCompositionLocal
|
||||
f:org.jetbrains.jewel.bridge.theme.IntUiBridgeTextKt
|
||||
- *sf:retrieveDefaultTextStyle(F):androidx.compose.ui.text.TextStyle
|
||||
|
||||
@@ -71,12 +71,18 @@ f:org.jetbrains.jewel.bridge.JewelBridgeException$KeysNotFoundException
|
||||
- <init>(java.util.List,java.lang.String):V
|
||||
f:org.jetbrains.jewel.bridge.JewelComposePanelWrapperKt
|
||||
- sf:JewelComposePanel(kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
- sf:JewelComposePanel(Z,kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
- bs:JewelComposePanel$default(kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2,I,java.lang.Object):javax.swing.JComponent
|
||||
- bs:JewelComposePanel$default(Z,kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2,I,java.lang.Object):javax.swing.JComponent
|
||||
- sf:compose(kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
- sf:compose(Z,kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
- bs:compose$default(kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2,I,java.lang.Object):javax.swing.JComponent
|
||||
- bs:compose$default(Z,kotlin.jvm.functions.Function1,kotlin.jvm.functions.Function2,I,java.lang.Object):javax.swing.JComponent
|
||||
f:org.jetbrains.jewel.bridge.ToolWindowExtensionsKt
|
||||
- sf:addComposeTab(com.intellij.openapi.wm.ToolWindow,java.lang.String,Z,Z,kotlin.jvm.functions.Function3):V
|
||||
- sf:addComposeTab(com.intellij.openapi.wm.ToolWindow,java.lang.String,Z,Z,Z,kotlin.jvm.functions.Function3):V
|
||||
- bs:addComposeTab$default(com.intellij.openapi.wm.ToolWindow,java.lang.String,Z,Z,kotlin.jvm.functions.Function3,I,java.lang.Object):V
|
||||
- bs:addComposeTab$default(com.intellij.openapi.wm.ToolWindow,java.lang.String,Z,Z,Z,kotlin.jvm.functions.Function3,I,java.lang.Object):V
|
||||
org.jetbrains.jewel.bridge.ToolWindowScope
|
||||
- a:getToolWindow():com.intellij.openapi.wm.ToolWindow
|
||||
f:org.jetbrains.jewel.bridge.TypographyKt
|
||||
|
||||
+138
-29
@@ -7,11 +7,15 @@ import androidx.compose.ui.awt.ComposePanel
|
||||
import com.intellij.openapi.actionSystem.DataSink
|
||||
import com.intellij.openapi.actionSystem.UiDataProvider
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import java.awt.BorderLayout
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import java.awt.AWTEvent
|
||||
import java.awt.Component
|
||||
import java.awt.Toolkit
|
||||
import java.awt.event.AWTEventListener
|
||||
import java.awt.event.MouseEvent
|
||||
import java.io.File
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.SwingUtilities
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.jewel.bridge.actionSystem.ComponentDataProviderBridge
|
||||
import org.jetbrains.jewel.bridge.component.JBPopupRenderer
|
||||
@@ -27,11 +31,28 @@ import org.jetbrains.jewel.ui.util.LocalMessageResourceResolverProvider
|
||||
*
|
||||
* The [content] is wrapped in a [SwingBridgeTheme], which will be derived from the current Swing LaF.
|
||||
*
|
||||
* @param focusOnClickInside If `true`, the underlying [ComposePanel] will request focus when a mouse click occurs
|
||||
* inside it, even if it does not hit a "focusable" element.
|
||||
* @param config A lambda to configure the underlying [ComposePanel].
|
||||
* @param content The Composable content to display.
|
||||
*/
|
||||
public fun compose(
|
||||
focusOnClickInside: Boolean = true,
|
||||
config: ComposePanel.() -> Unit = {},
|
||||
content: @Composable () -> Unit,
|
||||
): JComponent = JewelComposePanel(focusOnClickInside, config, content)
|
||||
|
||||
/**
|
||||
* Creates a Swing component that can host Compose content.
|
||||
*
|
||||
* The [content] is wrapped in a [SwingBridgeTheme], which will be derived from the current Swing LaF.
|
||||
*
|
||||
* @param config A lambda to configure the underlying [ComposePanel].
|
||||
* @param content The Composable content to display.
|
||||
*/
|
||||
@Deprecated("Use the version with 'focusOnClickInside' parameter")
|
||||
public fun compose(config: ComposePanel.() -> Unit = {}, content: @Composable () -> Unit): JComponent =
|
||||
JewelComposePanel(config, content)
|
||||
JewelComposePanel(focusOnClickInside = false, config, content)
|
||||
|
||||
/**
|
||||
* Creates a Swing component that can host Compose content.
|
||||
@@ -40,12 +61,18 @@ public fun compose(config: ComposePanel.() -> Unit = {}, content: @Composable ()
|
||||
*
|
||||
* This is the same as [compose].
|
||||
*
|
||||
* @param focusOnClickInside If `true`, the underlying [ComposePanel] will request focus when a mouse click occurs
|
||||
* inside it, even if it does not hit a "focusable" element.
|
||||
* @param config A lambda to configure the underlying [ComposePanel].
|
||||
* @param content The Composable content to display.
|
||||
*/
|
||||
@Suppress("ktlint:standard:function-naming", "FunctionName") // Swing to Compose bridge API
|
||||
public fun JewelComposePanel(config: ComposePanel.() -> Unit = {}, content: @Composable () -> Unit): JComponent =
|
||||
createJewelComposePanel { jewelPanel ->
|
||||
public fun JewelComposePanel(
|
||||
focusOnClickInside: Boolean = true,
|
||||
config: ComposePanel.() -> Unit = {},
|
||||
content: @Composable () -> Unit,
|
||||
): JComponent =
|
||||
createJewelComposePanel(focusOnClickInside) { jewelPanel ->
|
||||
config()
|
||||
setContent {
|
||||
SwingBridgeTheme {
|
||||
@@ -59,6 +86,41 @@ public fun JewelComposePanel(config: ComposePanel.() -> Unit = {}, content: @Com
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Swing component that can host Compose content.
|
||||
*
|
||||
* The [content] is wrapped in a [SwingBridgeTheme], which will be derived from the current Swing LaF.
|
||||
*
|
||||
* This is the same as [compose].
|
||||
*
|
||||
* @param config A lambda to configure the underlying [ComposePanel].
|
||||
* @param content The Composable content to display.
|
||||
*/
|
||||
@Suppress("ktlint:standard:function-naming", "FunctionName") // Swing to Compose bridge API
|
||||
@Deprecated("Use the version with 'focusOnClickInside' parameter")
|
||||
public fun JewelComposePanel(config: ComposePanel.() -> Unit = {}, content: @Composable () -> Unit): JComponent =
|
||||
JewelComposePanel(focusOnClickInside = false, config, content)
|
||||
|
||||
/**
|
||||
* Creates a Swing component that can host Compose content.
|
||||
*
|
||||
* The [content] is **not** wrapped in a theme, meaning that you **MUST** wrap the content in a theme by yourself.
|
||||
*
|
||||
* This is not normally what you want; use this only if you want to provide a completely custom theme.
|
||||
*
|
||||
* @param focusOnClickInside If `true`, the underlying [ComposePanel] will request focus when a mouse click occurs
|
||||
* inside it, even if it does not hit a "focusable" element.
|
||||
* @param config A lambda to configure the underlying [ComposePanel].
|
||||
* @param content The Composable content to display.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ExperimentalJewelApi
|
||||
public fun composeWithoutTheme(
|
||||
focusOnClickInside: Boolean = true,
|
||||
config: ComposePanel.() -> Unit = {},
|
||||
content: @Composable () -> Unit,
|
||||
): JComponent = JewelComposeNoThemePanel(focusOnClickInside, config, content)
|
||||
|
||||
/**
|
||||
* Creates a Swing component that can host Compose content.
|
||||
*
|
||||
@@ -72,8 +134,44 @@ public fun JewelComposePanel(config: ComposePanel.() -> Unit = {}, content: @Com
|
||||
@ApiStatus.Experimental
|
||||
@ExperimentalJewelApi
|
||||
@Suppress("ktlint:standard:function-naming") // Swing to Compose bridge API
|
||||
@Deprecated("Use the version with 'focusOnClickInside' parameter")
|
||||
public fun composeWithoutTheme(config: ComposePanel.() -> Unit = {}, content: @Composable () -> Unit): JComponent =
|
||||
JewelComposeNoThemePanel(config, content)
|
||||
JewelComposeNoThemePanel(focusOnClickInside = false, config, content)
|
||||
|
||||
/**
|
||||
* Creates a Swing component that can host Compose content.
|
||||
*
|
||||
* The [content] is **not** wrapped in a theme, meaning that you **MUST** wrap the content in a theme by yourself.
|
||||
*
|
||||
* This is not normally what you want; use this only if you want to provide a completely custom theme.
|
||||
*
|
||||
* This is the same as [composeWithoutTheme].
|
||||
*
|
||||
* @param focusOnClickInside If `true`, the underlying [ComposePanel] will request focus when a mouse click occurs
|
||||
* inside it, even if it does not hit a "focusable" element.
|
||||
* @param config A lambda to configure the underlying [ComposePanel].
|
||||
* @param content The Composable content to display.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ExperimentalJewelApi
|
||||
@Suppress("ktlint:standard:function-naming", "FunctionName") // Swing to Compose bridge API
|
||||
public fun JewelComposeNoThemePanel(
|
||||
focusOnClickInside: Boolean = true,
|
||||
config: ComposePanel.() -> Unit = {},
|
||||
content: @Composable () -> Unit,
|
||||
): JComponent =
|
||||
createJewelComposePanel(focusOnClickInside) { jewelPanel ->
|
||||
config()
|
||||
setContent {
|
||||
CompositionLocalProvider(
|
||||
LocalComponentFoundation provides this@createJewelComposePanel,
|
||||
LocalPopupRenderer provides JBPopupRenderer,
|
||||
LocalMessageResourceResolverProvider provides BridgeMessageResourceResolver(),
|
||||
) {
|
||||
ComponentDataProviderBridge(jewelPanel, content = content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Swing component that can host Compose content.
|
||||
@@ -90,21 +188,14 @@ public fun composeWithoutTheme(config: ComposePanel.() -> Unit = {}, content: @C
|
||||
@ApiStatus.Experimental
|
||||
@ExperimentalJewelApi
|
||||
@Suppress("ktlint:standard:function-naming", "FunctionName") // Swing to Compose bridge API
|
||||
@Deprecated("Use the version with 'focusOnClickInside' parameter")
|
||||
public fun JewelComposeNoThemePanel(config: ComposePanel.() -> Unit = {}, content: @Composable () -> Unit): JComponent =
|
||||
createJewelComposePanel { jewelPanel ->
|
||||
config()
|
||||
setContent {
|
||||
CompositionLocalProvider(
|
||||
LocalComponentFoundation provides this@createJewelComposePanel,
|
||||
LocalPopupRenderer provides JBPopupRenderer,
|
||||
LocalMessageResourceResolverProvider provides BridgeMessageResourceResolver(),
|
||||
) {
|
||||
ComponentDataProviderBridge(jewelPanel, content = content)
|
||||
}
|
||||
}
|
||||
}
|
||||
JewelComposeNoThemePanel(focusOnClickInside = false, config, content)
|
||||
|
||||
private fun createJewelComposePanel(config: ComposePanel.(JewelComposePanelWrapper) -> Unit): JewelComposePanelWrapper {
|
||||
private fun createJewelComposePanel(
|
||||
focusOnClickInside: Boolean,
|
||||
config: ComposePanel.(JewelComposePanelWrapper) -> Unit,
|
||||
): JewelComposePanelWrapper {
|
||||
if (System.getProperty("skiko.library.path") == null) {
|
||||
val bundledSkikoFolder = File(PathManager.getLibPath(), "/skiko-awt-runtime-all")
|
||||
if (bundledSkikoFolder.isDirectory && bundledSkikoFolder.canRead()) {
|
||||
@@ -113,22 +204,26 @@ private fun createJewelComposePanel(config: ComposePanel.(JewelComposePanelWrapp
|
||||
JewelLogger.getInstance("SkikoLoader").warn("Bundled Skiko not found/not readable, falling back to default")
|
||||
}
|
||||
}
|
||||
val jewelPanel = JewelComposePanelWrapper()
|
||||
jewelPanel.layout = BorderLayout()
|
||||
val composePanel = ComposePanel()
|
||||
jewelPanel.add(composePanel, BorderLayout.CENTER)
|
||||
composePanel.config(jewelPanel)
|
||||
val jewelPanel = JewelComposePanelWrapper(focusOnClickInside)
|
||||
jewelPanel.composePanel.config(jewelPanel)
|
||||
ComposeUiInspector(jewelPanel)
|
||||
return jewelPanel
|
||||
}
|
||||
|
||||
internal class JewelComposePanelWrapper : JPanel(), UiDataProvider {
|
||||
internal class JewelComposePanelWrapper(private val focusOnClickInside: Boolean) : BorderLayoutPanel(), UiDataProvider {
|
||||
internal var targetProvider: UiDataProvider? = null
|
||||
private val listener = AWTEventListener { event ->
|
||||
if (event !is MouseEvent || event.button == MouseEvent.NOBUTTON) return@AWTEventListener
|
||||
if (!isFocusOwner && SwingUtilities.isDescendingFrom(event.component, this)) {
|
||||
composePanel.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
val composePanel: ComposePanel
|
||||
get() =
|
||||
components.singleOrNull() as? ComposePanel
|
||||
?: error("JewelComposePanelWrapper was not initialized with a ComposePanel")
|
||||
val composePanel: ComposePanel = ComposePanel()
|
||||
|
||||
init {
|
||||
super.addToCenter(composePanel)
|
||||
}
|
||||
|
||||
override fun addImpl(comp: Component, constraints: Any?, index: Int) {
|
||||
require(components.isEmpty()) {
|
||||
@@ -142,6 +237,20 @@ internal class JewelComposePanelWrapper : JPanel(), UiDataProvider {
|
||||
super.addImpl(comp, constraints, index)
|
||||
}
|
||||
|
||||
override fun addNotify() {
|
||||
super.addNotify()
|
||||
if (focusOnClickInside) {
|
||||
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.MOUSE_EVENT_MASK)
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeNotify() {
|
||||
super.removeNotify()
|
||||
if (focusOnClickInside) {
|
||||
Toolkit.getDefaultToolkit().removeAWTEventListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun uiDataSnapshot(sink: DataSink) {
|
||||
targetProvider?.uiDataSnapshot(sink)
|
||||
}
|
||||
|
||||
+22
-1
@@ -8,6 +8,8 @@ import org.jetbrains.jewel.foundation.enableNewSwingCompositing
|
||||
/**
|
||||
* Adds a new tab to the tool window with Compose content.
|
||||
*
|
||||
* @param focusOnClickInside If `true`, the underlying [ComposePanel] will request focus when a mouse click occurs
|
||||
* inside it, even if it does not hit a "focusable" element.
|
||||
* @param tabDisplayName The title of the tab.
|
||||
* @param isLockable Whether the tab can be locked.
|
||||
* @param isCloseable Whether the tab can be closed.
|
||||
@@ -17,6 +19,7 @@ public fun ToolWindow.addComposeTab(
|
||||
@TabTitle tabDisplayName: String? = null,
|
||||
isLockable: Boolean = true,
|
||||
isCloseable: Boolean = false,
|
||||
focusOnClickInside: Boolean = true,
|
||||
content: @Composable ToolWindowScope.() -> Unit,
|
||||
) {
|
||||
// We need to make sure this is done before Compose is attached.
|
||||
@@ -25,7 +28,7 @@ public fun ToolWindow.addComposeTab(
|
||||
|
||||
val tabContent =
|
||||
contentManager.factory.createContent(
|
||||
JewelComposePanel {
|
||||
JewelComposePanel(focusOnClickInside) {
|
||||
val scope =
|
||||
object : ToolWindowScope {
|
||||
override val toolWindow: ToolWindow
|
||||
@@ -40,6 +43,24 @@ public fun ToolWindow.addComposeTab(
|
||||
contentManager.addContent(tabContent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new tab to the tool window with Compose content.
|
||||
*
|
||||
* @param tabDisplayName The title of the tab.
|
||||
* @param isLockable Whether the tab can be locked.
|
||||
* @param isCloseable Whether the tab can be closed.
|
||||
* @param content The Composable content of the tab.
|
||||
*/
|
||||
@Deprecated("Use the version with 'focusOnClickInside' parameter")
|
||||
public fun ToolWindow.addComposeTab(
|
||||
@TabTitle tabDisplayName: String? = null,
|
||||
isLockable: Boolean = true,
|
||||
isCloseable: Boolean = false,
|
||||
content: @Composable ToolWindowScope.() -> Unit,
|
||||
) {
|
||||
addComposeTab(tabDisplayName, isLockable, isCloseable, focusOnClickInside = true, content)
|
||||
}
|
||||
|
||||
/** A scope for the content of a tool window tab. */
|
||||
public interface ToolWindowScope {
|
||||
/** The tool window in which the tab is displayed. */
|
||||
|
||||
@@ -12,7 +12,10 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onFirstVisible
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
@@ -63,7 +66,7 @@ internal fun ComponentShowcaseTab(project: Project) {
|
||||
|
||||
@Composable
|
||||
private fun RowScope.ColumnOne() {
|
||||
Column(Modifier.trackActivation().weight(1f), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
var activated by remember { mutableStateOf(false) }
|
||||
Text(
|
||||
"Here is a selection of our finest components(activated: $activated):",
|
||||
@@ -72,10 +75,16 @@ private fun RowScope.ColumnOne() {
|
||||
)
|
||||
|
||||
var selectedItem by remember { mutableIntStateOf(-1) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
ListComboBox(
|
||||
items = remember { listOf("Hello", "World") },
|
||||
selectedIndex = selectedItem,
|
||||
onSelectedItemChange = { selectedItem = it },
|
||||
modifier =
|
||||
Modifier
|
||||
.focusRequester(focusRequester)
|
||||
.onFirstVisible { focusRequester.requestFocus() },
|
||||
)
|
||||
ListComboBox(
|
||||
items = remember { listOf("Hello", "World") },
|
||||
|
||||
@@ -16,13 +16,22 @@ internal class JewelDemoToolWindowFactory : ToolWindowFactory, DumbAware {
|
||||
// Enable custom popup rendering to use JBPopup instead of the default Compose implementation
|
||||
JewelFlags.useCustomPopupRenderer = true
|
||||
|
||||
toolWindow.addComposeTab(DevkitComposeBundle.message("jewel.tw.tab.title.components")) { ComponentShowcaseTab(project) }
|
||||
toolWindow.addComposeTab(
|
||||
DevkitComposeBundle.message("jewel.tw.tab.title.components"),
|
||||
focusOnClickInside = true,
|
||||
) { ComponentShowcaseTab(project) }
|
||||
|
||||
toolWindow.addComposeTab(DevkitComposeBundle.message("jewel.tw.tab.title.releases.demo")) { ReleasesSampleCompose(project) }
|
||||
toolWindow.addComposeTab(
|
||||
DevkitComposeBundle.message("jewel.tw.tab.title.releases.demo"),
|
||||
focusOnClickInside = true,
|
||||
) { ReleasesSampleCompose(project) }
|
||||
|
||||
toolWindow.addSwingTab(SwingComparisonTabPanel(), DevkitComposeBundle.message("jewel.tw.tab.title.swing.comparison"))
|
||||
|
||||
toolWindow.addComposeTab(DevkitComposeBundle.message("jewel.tw.tab.title.scrollbars.sample")) { ScrollbarsShowcaseTab() }
|
||||
toolWindow.addComposeTab(
|
||||
DevkitComposeBundle.message("jewel.tw.tab.title.scrollbars.sample"),
|
||||
focusOnClickInside = true,
|
||||
) { ScrollbarsShowcaseTab() }
|
||||
}
|
||||
|
||||
private fun ToolWindow.addSwingTab(component: JComponent, @TabTitle title: String) {
|
||||
|
||||
Reference in New Issue
Block a user