diff --git a/src/extension.ts b/src/extension.ts index 026de7e..9b064b0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -125,8 +125,8 @@ export default class TopBarOrganizerExtension extends Extension { /// Go through the items of the validBoxOrder and order the GNOME Shell /// top bar box accordingly. - for (let i = 0; i < validBoxOrder.length; i++) { - const item = validBoxOrder[i]; + const indicatorContainers: St.Bin[] = []; + for (const item of validBoxOrder) { // Get the indicator container associated with the current role. const associatedIndicatorContainer = (Main.panel.statusArea as any)[item.role]?.container; if (!(associatedIndicatorContainer instanceof St.Bin)) { @@ -134,29 +134,23 @@ export default class TopBarOrganizerExtension extends Extension { continue; } + indicatorContainers.push(associatedIndicatorContainer); + // Save whether or not the indicator container is visible. const isVisible = associatedIndicatorContainer.visible; - const parent = associatedIndicatorContainer.get_parent(); - if (parent !== null) { - parent.remove_child(associatedIndicatorContainer); - } - if (box === "right") { - // If the target panel box is the right panel box, insert the - // indicator container at index `-1`, which just adds it to the - // end (correct order is ensured, since `validBoxOrder` is - // sorted correctly and we're looping over it in order). - // This way unaccounted-for indicator containers will be at the - // left, which is preferred, since the box is logically - // right-to-left. - // The same applies for indicator containers, which are just - // temporarily unaccounted for (like for indicator containers of - // not yet ready app indicators), since them being at the right - // for a probably temporary stay causes all the indicator - // containers to shift. + // Only remove the indicator container from its current parent if + // it actually changes boxes. `remove_child()` stops all running + // transitions on the removed actor and its children (the default + // remove-child flags include `REMOVE_CHILD_STOP_TRANSITIONS`). + // Detaching e.g. the "activities" indicator container while GNOME + // Shell animates in a new workspace dot stops that animation, + // leaving the dot invisible or incorrectly sized until the next + // workspace change. + if (associatedIndicatorContainer.get_parent() !== panelBox) { + const parent = associatedIndicatorContainer.get_parent(); + parent?.remove_child(associatedIndicatorContainer); panelBox.insert_child_at_index(associatedIndicatorContainer, -1); - } else { - panelBox.insert_child_at_index(associatedIndicatorContainer, i); } // Hide the indicator container... @@ -172,6 +166,24 @@ export default class TopBarOrganizerExtension extends Extension { associatedIndicatorContainer.hide(); } } + + // Order the indicator containers by changing their indexes in place. + // `set_child_at_index()` is logically equivalent to removing and + // re-inserting an actor, but unlike `remove_child()` it does not change + // the actor's state or stop its transitions. + // + // For the right box, the first indicator container has to be placed at + // the beginning of the block of accounted-for indicator containers at + // the end of the box. This way unaccounted-for indicator containers + // (e.g. not yet ready app indicators) will be at the left, which is + // preferred, since the box is logically right-to-left. For the left and + // center box, the block starts at index 0. + const firstIndex = box === "right" + ? panelBox.get_n_children() - indicatorContainers.length + : 0; + for (let i = 0; i < indicatorContainers.length; i++) { + panelBox.set_child_at_index(indicatorContainers[i], firstIndex + i); + } // To handle the case, where the box order got set to a permutation // of an outdated box order, it would be wise, if the caller updated the // box order now to include the items present in the top bar.