From 3012af4607d1ed07b8ff483ff4e18cbd01b60a80 Mon Sep 17 00:00:00 2001 From: Ivan Semenov Date: Thu, 5 Feb 2026 18:50:14 +0100 Subject: [PATCH] refactor [colab]: simplify chooser popups setup Drop custom listeners in favor of `launchOnShow` GitOrigin-RevId: 1c0fd2c9470508b401edc08b3d772192b8d6c272 --- .../api-dump-experimental.txt | 3 +- .../list/search/ChooserPopupUtil.kt | 152 +++++++----------- .../util/popup/CollaborationToolsPopupUtil.kt | 2 - 3 files changed, 57 insertions(+), 100 deletions(-) diff --git a/platform/collaboration-tools/api-dump-experimental.txt b/platform/collaboration-tools/api-dump-experimental.txt index 2b79090ad8ab..31f6bd5230b2 100644 --- a/platform/collaboration-tools/api-dump-experimental.txt +++ b/platform/collaboration-tools/api-dump-experimental.txt @@ -1635,8 +1635,7 @@ f:com.intellij.collaboration.ui.codereview.diff.viewer.DiffViewerUtilKt - sf:awaitClose(com.intellij.openapi.ui.popup.JBPopup,kotlin.coroutines.Continuation):java.lang.Object - sf:showAndAwait(com.intellij.openapi.ui.popup.JBPopup,com.intellij.ui.awt.RelativePoint,com.intellij.collaboration.ui.codereview.list.search.ShowDirection,kotlin.coroutines.Continuation):java.lang.Object - sf:showAndAwaitListSubmission(com.intellij.openapi.ui.popup.JBPopup,com.intellij.ui.awt.RelativePoint,com.intellij.collaboration.ui.codereview.list.search.ShowDirection,kotlin.coroutines.Continuation):java.lang.Object -- sf:showAndAwaitSubmission(com.intellij.openapi.ui.popup.JBPopup,javax.swing.JList,com.intellij.ui.awt.RelativePoint,com.intellij.collaboration.ui.codereview.list.search.ShowDirection,kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object -- bs:showAndAwaitSubmission$default(com.intellij.openapi.ui.popup.JBPopup,javax.swing.JList,com.intellij.ui.awt.RelativePoint,com.intellij.collaboration.ui.codereview.list.search.ShowDirection,kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation,I,java.lang.Object):java.lang.Object +- sf:showAndAwaitSubmission(com.intellij.openapi.ui.popup.JBPopup,javax.swing.JList,com.intellij.ui.awt.RelativePoint,com.intellij.collaboration.ui.codereview.list.search.ShowDirection,kotlin.coroutines.Continuation):java.lang.Object - sf:showAndAwaitSubmissions(com.intellij.openapi.ui.popup.JBPopup,javax.swing.ListModel,com.intellij.ui.awt.RelativePoint,com.intellij.collaboration.ui.codereview.list.search.ShowDirection,kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation):java.lang.Object - bs:showAndAwaitSubmissions$default(com.intellij.openapi.ui.popup.JBPopup,javax.swing.ListModel,com.intellij.ui.awt.RelativePoint,com.intellij.collaboration.ui.codereview.list.search.ShowDirection,kotlin.jvm.functions.Function0,kotlin.coroutines.Continuation,I,java.lang.Object):java.lang.Object *:com.intellij.collaboration.ui.util.popup.PopupItemPresentation diff --git a/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/list/search/ChooserPopupUtil.kt b/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/list/search/ChooserPopupUtil.kt index e6e8fbe68dc4..511d8a095fb7 100644 --- a/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/list/search/ChooserPopupUtil.kt +++ b/platform/collaboration-tools/src/com/intellij/collaboration/ui/codereview/list/search/ChooserPopupUtil.kt @@ -4,6 +4,7 @@ package com.intellij.collaboration.ui.codereview.list.search import com.intellij.collaboration.messages.CollaborationToolsBundle import com.intellij.collaboration.ui.codereview.details.SelectableWrapper import com.intellij.collaboration.ui.codereview.list.error.ErrorStatusPresenter +import com.intellij.collaboration.ui.codereview.list.search.ChooserPopupUtil.showAsyncChooserPopup import com.intellij.collaboration.ui.util.name import com.intellij.collaboration.ui.util.popup.CollaborationToolsPopupUtil import com.intellij.collaboration.ui.util.popup.PopupItemPresentation @@ -12,8 +13,9 @@ import com.intellij.collaboration.ui.util.popup.SimplePopupItemRenderer import com.intellij.collaboration.ui.util.popup.SimpleSelectablePopupItemRenderer import com.intellij.collaboration.ui.util.popup.showAndAwaitSubmission import com.intellij.collaboration.ui.util.popup.showAndAwaitSubmissions +import com.intellij.collaboration.util.ComputedResult +import com.intellij.collaboration.util.fold import com.intellij.openapi.ui.popup.JBPopup -import com.intellij.openapi.ui.popup.JBPopupListener import com.intellij.openapi.ui.popup.PopupChooserBuilder import com.intellij.openapi.ui.popup.util.PopupUtil import com.intellij.openapi.util.NlsContexts @@ -24,8 +26,6 @@ import com.intellij.ui.components.JBList import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil import com.intellij.util.ui.launchOnShow -import com.intellij.collaboration.util.ComputedResult -import com.intellij.collaboration.util.fold import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow @@ -165,12 +165,21 @@ object ChooserPopupUtil { filteringMapper: (T) -> String, renderer: ListCellRenderer, popupConfig: PopupConfig = PopupConfig.DEFAULT, - ): T? = coroutineScope { + ): T? { val listModel = CollectionListModel() val list = createList(listModel, renderer) - val loadingListener = ListLoadingListener(itemsLoader, list, listModel, popupConfig.errorPresenter) + populateListOnShowFromResult(itemsLoader, list, listModel, popupConfig.errorPresenter) - showChooserPopupWithListener(point, filteringMapper, popupConfig, list, loadingListener) + @Suppress("UNCHECKED_CAST") + val popup = PopupChooserBuilder(list) + .setFilteringEnabled { filteringMapper(it as T) } + .configure(popupConfig) + .createPopup() + + CollaborationToolsPopupUtil.configureSearchField(popup, popupConfig) + PopupUtil.setPopupToggleComponent(popup, point.component) + + return popup.showAndAwaitSubmission(list, point, popupConfig.showDirection) } /** @@ -274,34 +283,39 @@ object ChooserPopupUtil { filteringMapper: (T) -> String, renderer: ListCellRenderer, popupConfig: PopupConfig = PopupConfig.DEFAULT, - ): T? = coroutineScope { + ): T? { val listModel = CollectionListModel() val list = createList(listModel, renderer) - val loadingListener = ComputedResultListLoadingListener(itemsState, list, listModel, popupConfig.errorPresenter) + list.launchOnShow("List items loader") { + itemsState.collect { computedResult -> + computedResult.fold( + onInProgress = { + list.setPaintBusy(true) + list.emptyText.clear() + }, + onSuccess = { items -> + val selected = list.selectedValue + listModel.replaceAll(items) + list.setSelectedValue(selected, true) + }, + onFailure = { exception -> + list.setPaintBusy(false) + showErrorOnPopupFailure(exception, popupConfig.errorPresenter, list) + } + ) + } + } - showChooserPopupWithListener(point, filteringMapper, popupConfig, list, loadingListener) - } - - private suspend fun showChooserPopupWithListener( - point: RelativePoint, - filteringMapper: (T) -> String, - popupConfig: PopupConfig, - list: JBList, - loadingListener: AsyncListLoadingListener - ): T? = coroutineScope { @Suppress("UNCHECKED_CAST") val popup = PopupChooserBuilder(list) .setFilteringEnabled { filteringMapper(it as T) } - .addListener(loadingListener) .configure(popupConfig) .createPopup() CollaborationToolsPopupUtil.configureSearchField(popup, popupConfig) PopupUtil.setPopupToggleComponent(popup, point.component) - popup.showAndAwaitSubmission(list, point, popupConfig.showDirection) { - loadingListener.afterShow(popup) - } + return popup.showAndAwaitSubmission(list, point, popupConfig.showDirection) } // Multiple options: @@ -314,7 +328,7 @@ object ChooserPopupUtil { presenter: (T) -> PopupItemPresentation, isOriginallySelected: (T) -> Boolean, popupConfig: PopupConfig = PopupConfig.DEFAULT, - ): List = coroutineScope { + ): List { val listModel = CollectionListModel>() val list = createSelectableList(listModel, SimpleSelectablePopupItemRenderer.create { item -> SelectablePopupItemPresentation.fromPresenter(presenter, item) @@ -326,9 +340,7 @@ object ChooserPopupUtil { } } } - val loadingListener = ListLoadingListener( - selectableBatchesFlow, list, listModel, popupConfig.errorPresenter - ) + populateListOnShowFromResult(selectableBatchesFlow, list, listModel, popupConfig.errorPresenter) @Suppress("UNCHECKED_CAST") val popup = PopupChooserBuilder(list) @@ -337,16 +349,13 @@ object ChooserPopupUtil { presenter(selectableItem.value).shortText } .setCloseOnEnter(false) - .addListener(loadingListener) .configure(popupConfig) .createPopup() CollaborationToolsPopupUtil.configureSearchField(popup, popupConfig) PopupUtil.setPopupToggleComponent(popup, point.component) - popup.showAndAwaitSubmissions(listModel, point, popupConfig.showDirection) { - loadingListener.afterShow(popup) - } + return popup.showAndAwaitSubmissions(listModel, point, popupConfig.showDirection) } private fun createList(listModel: CollectionListModel, renderer: ListCellRenderer): JBList = @@ -428,86 +437,37 @@ enum class ShowDirection { BELOW } -private interface AsyncListLoadingListener : JBPopupListener { - fun afterShow(popup: JBPopup) -} - -private abstract class AbstractListLoadingListener( - private val itemsFlow: Flow>>, - private val list: JBList, - protected val listModel: CollectionListModel, - private val errorPresenter: ErrorStatusPresenter.Text?, -) : AsyncListLoadingListener { - override fun afterShow(popup: JBPopup) { - popup.content.launchOnShow(javaClass.name) { - list.setPaintBusy(true) - list.emptyText.clear() - try { - itemsFlow.collect { resultedItems -> - resultedItems.fold( - onSuccess = { items -> onSuccess(items) }, - onFailure = { exception -> - showErrorOnPopupFailure(exception, errorPresenter, list) - } - ) - } - } - finally { - list.setPaintBusy(false) - } - } - } - - protected abstract fun onSuccess(items: List) -} - -private class ListLoadingListener( +private fun populateListOnShowFromResult( itemsFlow: Flow>>, - private val list: JBList, + list: JBList, listModel: CollectionListModel, errorPresenter: ErrorStatusPresenter.Text?, -) : AbstractListLoadingListener(itemsFlow, list, listModel, errorPresenter) { - - override fun onSuccess(items: List) { - val selected = list.selectedIndex - if (items.size > listModel.size) { - val newList = items.subList(listModel.size, items.size) - listModel.addAll(listModel.size, newList) - } - if (selected != -1) { - list.selectedIndex = selected - } - } -} - -private class ComputedResultListLoadingListener( - private val itemsState: StateFlow>>, - private val list: JBList, - private val listModel: CollectionListModel, - private val errorPresenter: ErrorStatusPresenter.Text?, -) : AsyncListLoadingListener { - override fun afterShow(popup: JBPopup) { - popup.content.launchOnShow(javaClass.name) { - itemsState.collect { computedResult -> - computedResult.fold( - onInProgress = { - list.setPaintBusy(true) - list.emptyText.clear() - }, +) { + list.launchOnShow("List items loader") { + list.setPaintBusy(true) + list.emptyText.clear() + try { + itemsFlow.collect { resultedItems -> + resultedItems.fold( onSuccess = { items -> val selected = list.selectedIndex - listModel.replaceAll(items) - if (selected != -1 && selected < listModel.size) { + if (items.size > listModel.size) { + val newList = items.subList(listModel.size, items.size) + listModel.addAll(listModel.size, newList) + } + if (selected != -1) { list.selectedIndex = selected } }, onFailure = { exception -> - list.setPaintBusy(false) showErrorOnPopupFailure(exception, errorPresenter, list) } ) } } + finally { + list.setPaintBusy(false) + } } } diff --git a/platform/collaboration-tools/src/com/intellij/collaboration/ui/util/popup/CollaborationToolsPopupUtil.kt b/platform/collaboration-tools/src/com/intellij/collaboration/ui/util/popup/CollaborationToolsPopupUtil.kt index 30c10975e9aa..ed7ac71dd2f6 100644 --- a/platform/collaboration-tools/src/com/intellij/collaboration/ui/util/popup/CollaborationToolsPopupUtil.kt +++ b/platform/collaboration-tools/src/com/intellij/collaboration/ui/util/popup/CollaborationToolsPopupUtil.kt @@ -76,10 +76,8 @@ suspend fun JBPopup.showAndAwaitSubmission( list: JList, point: RelativePoint, showDirection: ShowDirection, - afterShow: () -> Unit = { }, ): T? { showPopup(point, showDirection) - afterShow() return waitForChoiceAsync(list) }