diff --git a/intellij-plugin/hs-core/resources/HyperskillAcademyPlugin.themeMetadata.json b/intellij-plugin/hs-core/resources/HyperskillAcademyPlugin.themeMetadata.json index cc7e03c76..f81d17b66 100644 --- a/intellij-plugin/hs-core/resources/HyperskillAcademyPlugin.themeMetadata.json +++ b/intellij-plugin/hs-core/resources/HyperskillAcademyPlugin.themeMetadata.json @@ -9,7 +9,7 @@ "source" : "org.hyperskill.academy.learning.newproject.ui.CourseCardComponent" }, { - "key" : "SelectCourse.CourseCard.hoverBackground", + "key" : "SelectCourse.CourseCard.selectedBackground", "description" : "The background color for a selected CourseCardComponent in Select Course dialog", "deprecated" : false, "source" : "org.hyperskill.academy.learning.newproject.ui.CourseCardComponent" @@ -205,4 +205,4 @@ "source" : "org.hyperskill.academy.learning.ui.EduColors" } ] -} \ No newline at end of file +} diff --git a/intellij-plugin/hs-core/src/org/hyperskill/academy/learning/newproject/ui/CourseCardComponent.kt b/intellij-plugin/hs-core/src/org/hyperskill/academy/learning/newproject/ui/CourseCardComponent.kt index 768e98f67..170f91b41 100644 --- a/intellij-plugin/hs-core/src/org/hyperskill/academy/learning/newproject/ui/CourseCardComponent.kt +++ b/intellij-plugin/hs-core/src/org/hyperskill/academy/learning/newproject/ui/CourseCardComponent.kt @@ -22,7 +22,7 @@ private const val CARD_HEIGHT = 70 private const val LOGO_SIZE = 40 private val HOVER_COLOR: Color = JBColor.namedColor("SelectCourse.CourseCard.hoverBackground", 0xF5F9FF, 0x282A2C) -private val SELECTION_COLOR: Color = JBColor.namedColor("SelectCourse.CourseCard.hoverBackground", 0xE9EEF5, 0x36393B) +private val SELECTION_COLOR: Color = JBColor.namedColor("SelectCourse.CourseCard.selectedBackground", 0xE9EEF5, 0x36393B) val GRAY_COLOR: Color = JBColor.namedColor("SelectCourse.grayForeground", JBColor(Gray._120, Gray._135)) open class CourseCardComponent(val course: Course) : JPanel(BorderLayout()) { @@ -125,4 +125,4 @@ private class CourseNameComponent(course: Course) : JPanel(BorderLayout()) { add(nameLabel, BorderLayout.CENTER) } -} \ No newline at end of file +} diff --git a/intellij-plugin/hs-core/src/org/hyperskill/academy/learning/newproject/ui/coursePanel/groups/CourseGroupModel.kt b/intellij-plugin/hs-core/src/org/hyperskill/academy/learning/newproject/ui/coursePanel/groups/CourseGroupModel.kt index ec9af8ceb..408226f16 100644 --- a/intellij-plugin/hs-core/src/org/hyperskill/academy/learning/newproject/ui/coursePanel/groups/CourseGroupModel.kt +++ b/intellij-plugin/hs-core/src/org/hyperskill/academy/learning/newproject/ui/coursePanel/groups/CourseGroupModel.kt @@ -120,10 +120,11 @@ class CourseGroupModel { private inner class HoverMouseHandler : MouseAdapter() { override fun mouseExited(event: MouseEvent) { + val hadHoveredCard = hoveredCard != null hoveredCard?.setSelection(false) hoveredCard?.onHoverEnded() - if (hoveredCard == null) { + if (!hadHoveredCard) { selectedCard?.onHoverEnded() } hoveredCard = null @@ -132,8 +133,21 @@ class CourseGroupModel { override fun mouseMoved(event: MouseEvent) { val cardComponent = getCourseCard(event) if (cardComponent != selectedCard) { + if (hoveredCard !== cardComponent) { + val previousHoveredCard = hoveredCard + previousHoveredCard?.setSelection(false) + previousHoveredCard?.onHoverEnded() + if (previousHoveredCard == null) { + selectedCard?.onHoverEnded() + } + } hoveredCard = cardComponent } + else if (hoveredCard != null) { + hoveredCard?.setSelection(false) + hoveredCard?.onHoverEnded() + hoveredCard = null + } cardComponent?.onHover(cardComponent == selectedCard) } @@ -167,4 +181,4 @@ class CourseGroupModel { } } } -} \ No newline at end of file +} diff --git a/intellij-plugin/hs-core/testSrc/org/hyperskill/academy/learning/newproject/ui/coursePanel/groups/CourseGroupModelTest.kt b/intellij-plugin/hs-core/testSrc/org/hyperskill/academy/learning/newproject/ui/coursePanel/groups/CourseGroupModelTest.kt new file mode 100644 index 000000000..524ed269d --- /dev/null +++ b/intellij-plugin/hs-core/testSrc/org/hyperskill/academy/learning/newproject/ui/coursePanel/groups/CourseGroupModelTest.kt @@ -0,0 +1,52 @@ +package org.hyperskill.academy.learning.newproject.ui.coursePanel.groups + +import org.hyperskill.academy.learning.EduTestCase +import org.hyperskill.academy.learning.courseFormat.hyperskill.HyperskillCourse +import org.hyperskill.academy.learning.newproject.ui.CourseCardComponent +import org.junit.Test +import java.awt.event.MouseEvent + +class CourseGroupModelTest : EduTestCase() { + + @Test + fun `test moving mouse between cards clears previous hover without exit event`() { + val model = CourseGroupModel() + val selectedCard = TestCourseCard("Selected course") + val firstHoveredCard = TestCourseCard("First hovered course") + val secondHoveredCard = TestCourseCard("Second hovered course") + listOf(selectedCard, firstHoveredCard, secondHoveredCard).forEach(model::addCourseCard) + model.initialSelection() + + moveMouseOver(selectedCard) + assertTrue(selectedCard.isHovered) + + moveMouseOver(firstHoveredCard) + assertFalse(selectedCard.isHovered) + assertTrue(firstHoveredCard.isHovered) + + moveMouseOver(secondHoveredCard) + assertFalse(selectedCard.isHovered) + assertFalse(firstHoveredCard.isHovered) + assertTrue(secondHoveredCard.isHovered) + } + + private fun moveMouseOver(card: CourseCardComponent) { + val component = card.getClickComponent() + val event = MouseEvent(component, MouseEvent.MOUSE_MOVED, 0, 0, 0, 0, 0, false) + component.mouseMotionListeners.forEach { it.mouseMoved(event) } + } + + private class TestCourseCard(courseName: String) : CourseCardComponent(HyperskillCourse().apply { name = courseName }) { + var isHovered: Boolean = false + private set + + override fun onHover(isSelected: Boolean) { + super.onHover(isSelected) + isHovered = true + } + + override fun onHoverEnded() { + isHovered = false + } + } +}