diff --git a/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/timeline/comment/CommentInputComponentFactory.kt b/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/timeline/comment/CommentInputComponentFactory.kt deleted file mode 100644 index 4ff1b778074f..000000000000 --- a/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/timeline/comment/CommentInputComponentFactory.kt +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.collaboration.ui.codereview.timeline.comment - -import com.intellij.collaboration.ui.CollaborationToolsUIUtil -import com.intellij.collaboration.ui.codereview.CodeReviewChatItemUIUtil -import com.intellij.collaboration.ui.icon.IconsProvider -import com.intellij.ui.scale.JBUIScale -import com.intellij.util.ui.JBInsets -import org.jetbrains.annotations.Nls -import java.awt.* -import javax.swing.JComponent -import javax.swing.JLabel -import javax.swing.JPanel -import kotlin.math.max -import kotlin.math.min - -object CommentInputComponentFactory { - fun addIconLeft(componentType: CodeReviewChatItemUIUtil.ComponentType, item: JComponent, - iconProvider: IconsProvider, iconKey: T, iconTooltip: @Nls String? = null): JComponent { - val iconLabel = JLabel(iconProvider.getIcon(iconKey, componentType.iconSize)).apply { - toolTipText = iconTooltip - } - - return JPanel(CommentFieldWithIconLayout(componentType.iconGap - CollaborationToolsUIUtil.getFocusBorderInset())).apply { - isOpaque = false - add(CommentFieldWithIconLayout.ICON, iconLabel) - add(CommentFieldWithIconLayout.ITEM, item) - } - } -} - -/** - * Lays out the field with an icon on the left. - * Icon is aligned to the top of its column except when min height of the field is less than that of an icon, - * in this case avatar is centered along that min height. - * Same thing the other way around. - */ -private class CommentFieldWithIconLayout( - private val gap: Int -) : LayoutManager { - - companion object { - const val ICON = "ICON" - const val ITEM = "ITEM" - } - - private var iconComponent: Component? = null - private var itemComponent: Component? = null - - override fun addLayoutComponent(name: String, comp: Component?) { - when (name) { - ICON -> iconComponent = comp - ITEM -> itemComponent = comp - else -> error("Incorrect name $name") - } - } - - override fun removeLayoutComponent(comp: Component) { - if (iconComponent == comp) iconComponent = null - if (itemComponent == comp) itemComponent = null - } - - override fun preferredLayoutSize(parent: Container): Dimension = getSize(parent, Component::getPreferredSize) - override fun minimumLayoutSize(parent: Container): Dimension = getSize(parent, Component::getMinimumSize) - - private fun getSize(parent: Container, sizeGetter: (Component) -> Dimension?): Dimension { - val iconSize = iconComponent?.takeIf { it.isVisible }?.let(sizeGetter) ?: Dimension(0, 0) - val itemSize = itemComponent?.takeIf { it.isVisible }?.let(sizeGetter) ?: Dimension(0, 0) - - val gap = JBUIScale.scale(gap) - val i = parent.insets - - return Dimension(i.left + iconSize.width + gap + itemSize.width + i.right, - i.top + max(iconSize.height, itemSize.height) + i.bottom) - } - - override fun layoutContainer(parent: Container) { - val bounds = Rectangle(Point(0, 0), parent.size) - JBInsets.removeFrom(bounds, parent.insets) - var x = bounds.x - val y = bounds.y - var contentWidth = bounds.width - val contentHeight = bounds.height - - val iconHeight = iconComponent?.takeIf { it.isVisible }?.preferredSize?.height ?: 0 - val itemMinHeight = itemComponent?.takeIf { it.isVisible }?.minimumSize?.height ?: 0 - - iconComponent?.takeIf { it.isVisible }?.apply { - val prefSize = preferredSize - val width = min(contentWidth, prefSize.width) - setBounds(x, y + max(0, (itemMinHeight - iconHeight) / 2), width, min(contentHeight, prefSize.height)) - x += prefSize.width - x += JBUIScale.scale(gap) - - contentWidth -= width - contentWidth -= JBUIScale.scale(gap) - } - - itemComponent?.takeIf { it.isVisible }?.apply { - val maxSize = maximumSize - val minSize = minimumSize - - val width = if (contentWidth >= maxSize.width) { - maxSize.width - } - else { - if (contentWidth >= minSize.width) { - contentWidth - } - else { - minSize.width - } - } - - val height = if (contentHeight >= maxSize.height) { - maxSize.height - } - else { - if (contentHeight >= minSize.height) { - contentHeight - } - else { - minSize.height - } - } - - setBounds(x, y + max(0, (iconHeight - itemMinHeight) / 2), width, height) - } - } -} \ No newline at end of file diff --git a/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/timeline/comment/CommentTextFieldFactory.kt b/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/timeline/comment/CommentTextFieldFactory.kt index 429545a0f3a5..22be2f6bcc71 100644 --- a/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/timeline/comment/CommentTextFieldFactory.kt +++ b/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/timeline/comment/CommentTextFieldFactory.kt @@ -1,6 +1,9 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.collaboration.ui.codereview.timeline.comment +import com.intellij.collaboration.ui.CollaborationToolsUIUtil +import com.intellij.collaboration.ui.codereview.CodeReviewChatItemUIUtil +import com.intellij.collaboration.ui.icon.IconsProvider import com.intellij.openapi.actionSystem.PlatformCoreDataKeys import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.actions.IncrementalFindAction @@ -11,12 +14,18 @@ import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider import com.intellij.openapi.fileTypes.FileTypes import com.intellij.openapi.project.Project import com.intellij.ui.EditorTextField +import com.intellij.ui.scale.JBUIScale +import com.intellij.util.ui.JBInsets import com.intellij.util.ui.UIUtil import org.jetbrains.annotations.Nls -import java.awt.Rectangle +import java.awt.* import java.awt.event.ComponentAdapter import java.awt.event.ComponentEvent import javax.swing.JComponent +import javax.swing.JLabel +import javax.swing.JPanel +import kotlin.math.max +import kotlin.math.min object CommentTextFieldFactory { fun create( @@ -79,34 +88,147 @@ object CommentTextFieldFactory { class ScrollToComponent(val component: JComponent) : ScrollOnChangePolicy() } - private class CommentTextField( - project: Project?, - document: Document - ) : EditorTextField(document, project, FileTypes.PLAIN_TEXT) { - init { - isOneLineMode = false + fun wrapWithLeftIcon(componentType: CodeReviewChatItemUIUtil.ComponentType, item: JComponent, + iconProvider: IconsProvider, iconKey: T, iconTooltip: @Nls String? = null): JComponent { + val iconLabel = JLabel(iconProvider.getIcon(iconKey, componentType.iconSize)).apply { + toolTipText = iconTooltip } - //always paint pretty border - override fun updateBorder(editor: EditorEx) = setupBorder(editor) + return JPanel(CommentFieldWithIconLayout(componentType.iconGap - CollaborationToolsUIUtil.getFocusBorderInset())).apply { + isOpaque = false + add(CommentFieldWithIconLayout.ICON, iconLabel) + add(CommentFieldWithIconLayout.ITEM, item) + } + } +} - override fun createEditor(): EditorEx { - // otherwise border background is painted from multiple places - return super.createEditor().apply { - //TODO: fix in editor - //com.intellij.openapi.editor.impl.EditorImpl.getComponent() == non-opaque JPanel - // which uses default panel color - component.isOpaque = false - //com.intellij.ide.ui.laf.darcula.ui.DarculaEditorTextFieldBorder.paintBorder - scrollPane.isOpaque = false - } +private class CommentTextField( + project: Project?, + document: Document +) : EditorTextField(document, project, FileTypes.PLAIN_TEXT) { + init { + isOneLineMode = false + } + + //always paint pretty border + override fun updateBorder(editor: EditorEx) = setupBorder(editor) + + override fun createEditor(): EditorEx { + // otherwise border background is painted from multiple places + return super.createEditor().apply { + //TODO: fix in editor + //com.intellij.openapi.editor.impl.EditorImpl.getComponent() == non-opaque JPanel + // which uses default panel color + component.isOpaque = false + //com.intellij.ide.ui.laf.darcula.ui.DarculaEditorTextFieldBorder.paintBorder + scrollPane.isOpaque = false + } + } + + override fun getData(dataId: String): Any? { + if (PlatformCoreDataKeys.FILE_EDITOR.`is`(dataId)) { + return editor?.let { TextEditorProvider.getInstance().getTextEditor(it) } ?: super.getData(dataId) + } + return super.getData(dataId) + } +} + +/** + * Lays out the field with an icon on the left. + * Icon is aligned to the top of its column except when min height of the field is less than that of an icon, + * in this case avatar is centered along that min height. + * Same thing the other way around. + */ +private class CommentFieldWithIconLayout( + private val gap: Int +) : LayoutManager { + + companion object { + const val ICON = "ICON" + const val ITEM = "ITEM" + } + + private var iconComponent: Component? = null + private var itemComponent: Component? = null + + override fun addLayoutComponent(name: String, comp: Component?) { + when (name) { + ICON -> iconComponent = comp + ITEM -> itemComponent = comp + else -> error("Incorrect name $name") + } + } + + override fun removeLayoutComponent(comp: Component) { + if (iconComponent == comp) iconComponent = null + if (itemComponent == comp) itemComponent = null + } + + override fun preferredLayoutSize(parent: Container): Dimension = getSize(parent, Component::getPreferredSize) + override fun minimumLayoutSize(parent: Container): Dimension = getSize(parent, Component::getMinimumSize) + + private fun getSize(parent: Container, sizeGetter: (Component) -> Dimension?): Dimension { + val iconSize = iconComponent?.takeIf { it.isVisible }?.let(sizeGetter) ?: Dimension(0, 0) + val itemSize = itemComponent?.takeIf { it.isVisible }?.let(sizeGetter) ?: Dimension(0, 0) + + val gap = JBUIScale.scale(gap) + val i = parent.insets + + return Dimension(i.left + iconSize.width + gap + itemSize.width + i.right, + i.top + max(iconSize.height, itemSize.height) + i.bottom) + } + + override fun layoutContainer(parent: Container) { + val bounds = Rectangle(Point(0, 0), parent.size) + JBInsets.removeFrom(bounds, parent.insets) + var x = bounds.x + val y = bounds.y + var contentWidth = bounds.width + val contentHeight = bounds.height + + val iconHeight = iconComponent?.takeIf { it.isVisible }?.preferredSize?.height ?: 0 + val itemMinHeight = itemComponent?.takeIf { it.isVisible }?.minimumSize?.height ?: 0 + + iconComponent?.takeIf { it.isVisible }?.apply { + val prefSize = preferredSize + val width = min(contentWidth, prefSize.width) + setBounds(x, y + max(0, (itemMinHeight - iconHeight) / 2), width, min(contentHeight, prefSize.height)) + x += prefSize.width + x += JBUIScale.scale(gap) + + contentWidth -= width + contentWidth -= JBUIScale.scale(gap) } - override fun getData(dataId: String): Any? { - if (PlatformCoreDataKeys.FILE_EDITOR.`is`(dataId)) { - return editor?.let { TextEditorProvider.getInstance().getTextEditor(it) } ?: super.getData(dataId) + itemComponent?.takeIf { it.isVisible }?.apply { + val maxSize = maximumSize + val minSize = minimumSize + + val width = if (contentWidth >= maxSize.width) { + maxSize.width } - return super.getData(dataId) + else { + if (contentWidth >= minSize.width) { + contentWidth + } + else { + minSize.width + } + } + + val height = if (contentHeight >= maxSize.height) { + maxSize.height + } + else { + if (contentHeight >= minSize.height) { + contentHeight + } + else { + minSize.height + } + } + + setBounds(x, y + max(0, (iconHeight - itemMinHeight) / 2), width, height) } } } \ No newline at end of file diff --git a/plugins/github/src/org/jetbrains/plugins/github/pullrequest/comment/ui/GHCommentTextFieldFactory.kt b/plugins/github/src/org/jetbrains/plugins/github/pullrequest/comment/ui/GHCommentTextFieldFactory.kt index c888932de323..362dd04a725c 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/pullrequest/comment/ui/GHCommentTextFieldFactory.kt +++ b/plugins/github/src/org/jetbrains/plugins/github/pullrequest/comment/ui/GHCommentTextFieldFactory.kt @@ -5,8 +5,8 @@ import com.intellij.collaboration.ui.CollaborationToolsUIUtil import com.intellij.collaboration.ui.CollaborationToolsUIUtil.wrapWithProgressOverlay import com.intellij.collaboration.ui.codereview.CodeReviewChatItemUIUtil import com.intellij.collaboration.ui.codereview.comment.CommentInputActionsComponentFactory -import com.intellij.collaboration.ui.codereview.timeline.comment.CommentInputComponentFactory import com.intellij.collaboration.ui.codereview.timeline.comment.CommentTextFieldFactory.create +import com.intellij.collaboration.ui.codereview.timeline.comment.CommentTextFieldFactory.wrapWithLeftIcon import org.jetbrains.plugins.github.api.data.GHUser import org.jetbrains.plugins.github.ui.avatars.GHAvatarIconsProvider import javax.swing.JComponent @@ -23,9 +23,8 @@ class GHCommentTextFieldFactory(private val model: GHCommentTextFieldModel) { inputField } else { - CommentInputComponentFactory - .addIconLeft(avatar.componentType, inputField, - avatar.avatarIconsProvider, avatar.user.avatarUrl, avatar.user.getPresentableName()) + wrapWithLeftIcon(avatar.componentType, inputField, + avatar.avatarIconsProvider, avatar.user.avatarUrl, avatar.user.getPresentableName()) } return CommentInputActionsComponentFactory.attachActions(field, inputActions)