Skip to content

[IJPL-43620] [platform] Custom Ordering for Status Bar Items#3447

Open
jakub-bochenski wants to merge 2 commits intoJetBrains:masterfrom
jakub-bochenski:IJPL-43620
Open

[IJPL-43620] [platform] Custom Ordering for Status Bar Items#3447
jakub-bochenski wants to merge 2 commits intoJetBrains:masterfrom
jakub-bochenski:IJPL-43620

Conversation

@jakub-bochenski
Copy link
Copy Markdown

@jakub-bochenski jakub-bochenski commented Mar 5, 2026

At first I tried implementing user ordering using LoadingOrder:

  • currently the order of widgets in status bar is defined by plugins, using anchors like after LineSeparator
  • we want to allow the user to override this order
  • naive approach fails because, you might have two widgets A and B, typically B is after A but the user rearranges them, it will cause an exception if another widget has anchor like after A, before B
  • trying to override all the anchors and "linearize" them (sequential after anchors as per desired visual order), only helps assuming no new widgets are added,
    • even considering all currently available widgets (active and inactive) this will still fail if a new plugin that contributes status bar widgets is installed later
  • since widgets can be added and removed, there is a need for an algorithm to reconcile the order desired by the user and the default order defined by the plugins
  • there is no obvious, simple approach; this is a partial-order merge problem, this would require extending the DFSTBuilder.java class to support weighted edges

So I implemented a different approach.
User moves are recorded separately and applied after the LoadingOrder sorting is applied (WidgetSorter)

I didn't find any tests covering the status bar.
I've added a unit test to verify that the widgets are sorted correctly.

I've also fixed com.intellij.openapi.wm.impl.status.IdeStatusBarImplKt#wrap method to set WIDGET_IT.
Missing WIDGET_IT was preventing DnD from working.

jbochenski added 2 commits March 5, 2026 14:59
At first I tried implementing user ordering using LoadingOrder:
- currently the order of widgets in status bar is defined by plugins, using anchors like after LineSeparator
- we want to allow the user to override this order
- naive approach fails because, you might have two widgets A and B, typically B is after A but the user rearranges them, it will cause an exception if another widget has anchor like after A, before B
- trying to override all the anchors and "linearize" them (sequential after anchors as per desired visual order), only helps assuming no new widgets are added,
   - even considering all currently available widgets (active and inactive) this will still fail if a new plugin that contributes status bar widgets is installed later
- since widgets can be added and removed, there is a need for an algorithm to reconcile the order desired by the user and the default order defined by the plugins
- there is no obvious, simple approach; this is a partial-order merge problem, this would require extending the DFSTBuilder.java class to support weighted edges

So I implemented a different approach.
User moves are recorded separately and applied after the LoadingOrder sorting is applied (`WidgetSorter`)

I didn't find any tests covering the status bar.
I've added a unit test to verify that the widgets are sorted correctly.

I've also fixed `com.intellij.openapi.wm.impl.status.IdeStatusBarImplKt#wrap`
method to set WIDGET_IT.
Missing WIDGET_IT was preventing DnD from working.
Copy link
Copy Markdown

@aydarin aydarin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of storing user's customisations and applying them on top of the configured by xmls order.

If I get it correct, approach of storing moves by sourceWidgetID-> targetWidgetID won't handle reorder correctly if a widget with targetWidgetID will be removed.
What do you think about storing the state of customized order like: sourceWidgetID-> targetIndex?
We should store only changed indexes, and during reorder do a "bubble sort" by comparing targetIndex(lower indexes goes to left) without touching non-customised widgets.

For example:

Original: A-B-C-D
After drag D->A: D-A-B-C
Stored user reordering: D->0, A->1, B->2, C->3

Restore after the new widget is added:
Original: A-B-E-C-D
After reordering from the user state: D-A-B-E-C

Restore after one of them is removed:
Original: B-E-C-D
After reordering from the user state: D-B-E-C

What do you think?

val order: LoadingOrder
}
} No newline at end of file
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary change

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it? It will compile w/o it. but text files should end with new line by POSIX standards.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind keeping it.

return null
}

class WidgetSorter(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion to hide it from public API:

  @TestOnly
  class WidgetSorter(

data class StatusBarState(@JvmField val widgets: Map<String, Boolean> = emptyMap())
data class StatusBarState(
@JvmField val widgets: Map<String, Boolean> = emptyMap(),
@JvmField val userMoves: Map<String, String> = emptyMap(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIU, the order is important for user moves.
I guess they should be stored in the ordered data structure like list of pairs.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this does not apply if we store indexes instead

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi!
I'm not sure I understand the comment. Could you elaborate?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean if we apply you suggestion from #3447 (review) the stored data is different anyway

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If after discussion we decide to go that way, then yes.
It's still a proposal, not a requirement.

@jakub-bochenski
Copy link
Copy Markdown
Author

If I get it correct, approach of storing moves by sourceWidgetID-> targetWidgetID won't handle reorder correctly if a widget with targetWidgetID will be removed.

Yes, the order would reset for sourceWidget.

We should store only changed indexes, and during reorder do a "bubble sort" by comparing targetIndex(lower indexes goes to left) without touching non-customised widgets.

I'm not sure what would the index be during sort then if it's not stored?

@aydarin
Copy link
Copy Markdown

aydarin commented Apr 13, 2026

I'm not sure what would the index be during sort then if it's not stored?

Sorry, my example wasn't fully showing the idea. If we take longer example, then it's visible, we store only changed indexes without indexes for X, E and F.

Original: X-A-B-C-D-E-F
After drag D->A: X-D-A-B-C-E-F
Stored user reordering: D->0, A->1, B->2, C->3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants