Skip to content

Commit fa12350

Browse files
steelartintellij-monorepo-bot
authored andcommitted
IDEA-303671 Implement Drag-and-Drop for pinned run configurations
GitOrigin-RevId: ccd10ef03aae0e66fc9c363aecdec14d4e559cde
1 parent eb311f9 commit fa12350

File tree

3 files changed

+105
-5
lines changed

3 files changed

+105
-5
lines changed

platform/execution-impl/src/com/intellij/execution/ui/RunToolbarPopup.kt

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.intellij.execution.runners.ExecutionEnvironment
1212
import com.intellij.execution.runners.ProgramRunner
1313
import com.intellij.icons.AllIcons
1414
import com.intellij.ide.ActivityTracker
15+
import com.intellij.ide.dnd.*
1516
import com.intellij.openapi.actionSystem.*
1617
import com.intellij.openapi.actionSystem.ex.InlineActionsHolder
1718
import com.intellij.openapi.actionSystem.impl.PresentationFactory
@@ -32,6 +33,7 @@ import com.intellij.openapi.wm.ToolWindowId
3233
import com.intellij.psi.PsiFile
3334
import com.intellij.ui.ColorUtil
3435
import com.intellij.ui.components.JBList
36+
import com.intellij.ui.popup.ActionPopupStep
3537
import com.intellij.ui.popup.KeepingPopupOpenAction
3638
import com.intellij.ui.popup.PopupFactoryImpl
3739
import com.intellij.ui.popup.WizardPopup
@@ -83,7 +85,7 @@ internal fun createRunConfigurationsActionGroup(project: Project, e: AnActionEve
8385
HideableDefaultActionGroup(folderName) { shouldBeShown(null, it) }
8486
}
8587
val filteringSubActions: (RunnerAndConfigurationSettings, String) -> AnAction = { configuration, folderName ->
86-
createRunConfigurationWithInlines(runExecutor, debugExecutor, configuration, project, e) { holdingFilter ->
88+
createRunConfigurationWithInlines(runExecutor, debugExecutor, configuration, project, e, pinned) { holdingFilter ->
8789
holdingFilter && !recents.contains(configuration)
8890
}.also {
8991
it.templatePresentation.putClientProperty(Presentation.PROP_VALUE, folderName)
@@ -136,9 +138,15 @@ internal class RunConfigurationsActionGroupPopup(actionGroup: ActionGroup, dataC
136138
PopupFactoryImpl.ActionGroupPopup(null, actionGroup, dataContext, false, false, true, false,
137139
disposeCallback, 30, null, null, PresentationFactory(), false) {
138140

141+
private val dragRange: IntRange = 0 until RunConfigurationStartHistory.getInstance(project).pinned().size + 1
142+
139143
init {
140-
(list as? JBList<*>)?.setExpandableItemsEnabled(false)
141-
(list as? JBList<*>)?.dragEnabled = true
144+
list.setExpandableItemsEnabled(false)
145+
if (!dragRange.isEmpty()) {
146+
val dndManager = DnDManager.getInstance()
147+
dndManager.registerSource(MyDnDSource(), list, this)
148+
dndManager.registerTarget(MyDnDTarget(), list, this)
149+
}
142150
}
143151

144152
override fun createPopup(parent: WizardPopup?, step: PopupStep<*>?, parentValue: Any?): WizardPopup {
@@ -156,6 +164,63 @@ internal class RunConfigurationsActionGroupPopup(actionGroup: ActionGroup, dataC
156164
fun shouldBeShowing(action: AnAction, holdingFilter: Boolean): Boolean {
157165
return if (action is HideableAction) return action.shouldBeShown(holdingFilter) else true
158166
}
167+
168+
override fun getList(): JBList<*> {
169+
return super.getList() as JBList<*>
170+
}
171+
172+
private fun isPossibleToDragItem(index: Int): Boolean {
173+
return dragRange.contains(index)
174+
}
175+
176+
private data class DraggedIndex(val from: Int)
177+
178+
private inner class MyDnDTarget : DnDTarget {
179+
override fun update(aEvent: DnDEvent): Boolean {
180+
val from = (aEvent.attachedObject as? DraggedIndex)?.from
181+
if (from is Int) {
182+
val targetIndex: Int = list.locationToIndex(aEvent.point)
183+
val possible: Boolean = isPossibleToDragItem(targetIndex)
184+
list.setDropTargetIndex(if (possible && wouldActuallyMove(from, targetIndex)) targetIndex else -1)
185+
aEvent.isDropPossible = possible
186+
}
187+
else {
188+
aEvent.isDropPossible = false
189+
}
190+
return false
191+
}
192+
193+
override fun drop(aEvent: DnDEvent) {
194+
list.setDropTargetIndex(-1)
195+
val from = (aEvent.attachedObject as? DraggedIndex)?.from
196+
if (from is Int) {
197+
val targetIndex: Int = list.locationToIndex(aEvent.point)
198+
if (wouldActuallyMove(from, targetIndex)) {
199+
(step as ActionPopupStep).reorderItems(from, targetIndex, 0)
200+
RunConfigurationStartHistory.getInstance(project).reorderItems(from, targetIndex)
201+
listModel.syncModel()
202+
}
203+
}
204+
}
205+
206+
private fun wouldActuallyMove(from: Int, targetIndex: Int) = from != targetIndex && from + 1 != targetIndex
207+
208+
override fun cleanUpOnLeave() {
209+
list.setDropTargetIndex(-1)
210+
}
211+
}
212+
213+
private inner class MyDnDSource : DnDSource {
214+
override fun canStartDragging(action: DnDAction, dragOrigin: Point): Boolean {
215+
return isPossibleToDragItem(list.locationToIndex(dragOrigin))
216+
}
217+
218+
override fun startDragging(action: DnDAction, dragOrigin: Point): DnDDragStartBean? {
219+
val index: Int = list.locationToIndex(dragOrigin)
220+
if (index < 0) return null
221+
return DnDDragStartBean(DraggedIndex(index))
222+
}
223+
}
159224
}
160225

161226
private interface HideableAction {
@@ -460,6 +525,13 @@ class RunConfigurationStartHistory(private val project: Project) : PersistentSta
460525
_state = State(_state.history, newPinned, _state.allConfigurationsExpanded)
461526
}
462527

528+
fun reorderItems(from: Int, where: Int) {
529+
val list = _state.pinned.toMutableList()
530+
list.add(where, list[from])
531+
list.removeAt(if (where < from) from + 1 else from)
532+
_state = State(_state.history, list.toMutableSet(), _state.allConfigurationsExpanded)
533+
}
534+
463535
fun register(setting: RunnerAndConfigurationSettings) {
464536
_state = State(_state.history.take(max(5, _state.pinned.size + recentLimit*2)).toMutableList().apply {
465537
add(0, Element(setting.uniqueID))

platform/platform-impl/src/com/intellij/ui/popup/ActionPopupStep.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.intellij.util.containers.ContainerUtil;
1515
import com.intellij.util.ui.StatusText;
1616
import com.intellij.util.ui.UIUtil;
17+
import org.jetbrains.annotations.ApiStatus;
1718
import org.jetbrains.annotations.NotNull;
1819
import org.jetbrains.annotations.Nullable;
1920

@@ -320,6 +321,28 @@ public SpeedSearchFilter<PopupFactoryImpl.ActionItem> getSpeedSearchFilter() {
320321
return this;
321322
}
322323

324+
@ApiStatus.Internal
325+
public void reorderItems(int from, int where, int preserveSeparatorAt) {
326+
if (myItems.get(from).isPrependWithSeparator() || myItems.get(where).isPrependWithSeparator()) {
327+
String fromText = myItems.get(from).getSeparatorText();
328+
String whereText = myItems.get(where).getSeparatorText();
329+
myItems.get(from).setSeparatorText(null);
330+
if (preserveSeparatorAt == from) {
331+
myItems.get(from + 1).setSeparatorText(fromText);
332+
}
333+
if (preserveSeparatorAt == where) {
334+
myItems.get(where).setSeparatorText(null);
335+
myItems.get(from).setSeparatorText(whereText);
336+
}
337+
}
338+
PopupFactoryImpl.ActionItem toMove = myItems.get(from);
339+
myItems.add(where, toMove);
340+
if (where < from) {
341+
from ++;
342+
}
343+
myItems.remove(from);
344+
}
345+
323346
private static boolean isPopupOrMainMenuPlace(@NotNull String place) {
324347
return ActionPlaces.isPopupPlace(place) || ActionPlaces.MAIN_MENU.equals(place);
325348
}

platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -741,8 +741,8 @@ public static class ActionItem implements ShortcutProvider, AnActionHolder, Nume
741741
private final boolean myMnemonicsEnabled;
742742
private final boolean myHonorActionMnemonics;
743743

744-
private final boolean myPrependWithSeparator;
745-
private final @NlsContexts.Separator String mySeparatorText;
744+
boolean myPrependWithSeparator;
745+
private @NlsContexts.Separator String mySeparatorText;
746746

747747
@NotNull private final List<InlineActionItem> myInlineActions;
748748

@@ -870,6 +870,11 @@ public boolean isPrependWithSeparator() {
870870
return mySeparatorText;
871871
}
872872

873+
public void setSeparatorText(@NlsContexts.Separator String separatorText) {
874+
myPrependWithSeparator = separatorText != null;
875+
mySeparatorText = separatorText;
876+
}
877+
873878
public boolean isEnabled() { return myIsEnabled; }
874879

875880
public boolean isPerformGroup() { return myIsPerformGroup; }

0 commit comments

Comments
 (0)