We got the indicator out of the business of tracking connections,
now it's time to do the same for devices.
Let sections track device additions and removals, and create device
items for them as it sees fit.
It also allows the sections to handle the ::activation-failed signal
by themselves, instead of passing it on from device items.
With this, the indicator is now solely responsible for global state:
Manage the top bar indicators, notify on connection failures, and run
the portal helper when NetworkManager detects a captive portal.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
As mentioned when doing the same for VPN connections, it is currently
the indicator itself that keeps track of added and removed connections.
It then has to figure out the correct section, and iterate through all
its device items so each item can check whether the connection
corresponds to its device.
Stop that mess, and let each device item keep its connections updated
by itself. That is actually way easier, as NM.Device already exposes
available connections through a property, so we can get rid of all
the checkConnection() shenanigans.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
We currently wait until we got a connection to NetworkManager.
That's possible because the old PanelMenu indicator API takes
a menu, so it is possible to add or remove items dynamically
later.
That won't be the case with quick settings, where `quickSettingsItems`
is a plain array that is only read once when adding the indicator.
Prepare for that by moving section initialization into the constructor.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
Now that wi-fi devices will be handled by a separate menu toggle
instead of as part of a combined system menu, there is no longer
a need of delegating network selection to a separate dialog.
To keep the menu from growing too much, the (sorted) list of
displayed networks is kept at a limit of eight. There is always
Settings for a complete list…
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
In terms of UI, this means that in the common case where we have
a single (or no) connection, the device can be represented as a
single menu item rather than a submenu.
But more importantly, all our menu items inherit from the same
GObject class now, so we can use bindings where appropriate.
This will later extend to quick toggles as well.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
The class implements a menu item that contains a section which
can be collapsed into a submenu.
It is very common for network devices to only have one associated
connection, so hiding away a single item in a submenu is fairly
inconvenient.
This class will address this, by only using a submenu when it is
actually needed.
Although the main issue it addresses is that menus (including
sections) aren't GObjects. This gives us a GObject that can
be added to a menu, and that can itself contain other menu
items.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
The class only provides the GObject properties that are currently
defined by NMConnectionItem, plus a way to set the active connection
that should be used for the item's :is-active state.
Its sole purpose is to provide a shared base for both device- and
connection items, and to have that base be a GObject so we can
start linking properties via bindings rather than manual fiddling.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
Let's keep things simple and use the same appearance regardless
of the number of configured VPNs. Also unlike for device items,
every connection item in the VPN section is a toplevel item, so
there isn't a real need for different presentations.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
The NMConnectionSection class is used - surprise - to manage a
list of related connections. And while the presentation of VPN
items is slightly different from connections associated with
devices (switches vs. ornaments), it makes perfect sense for
the VPN section to share the nitty-gritty with the base class.
But…
Right now it is perfectly fine for NMConnectionSection to be
used both as a child element in a device section, and as toplevel
item of the VPN section. Any nesting of sections is entirely
transparent to the user, and all connection sections appear as
submenu items in the toplevel menu.
That won't work for quick settings.
There's no PopoverMenuSection that allows invisible grouping, so
adding items dynamically would either need to happen at the end,
or require some tricky cross-component code to impose a particular
order.
And last but not least, quick toggles are very much unsuited for
a potentially large number of items. The whole point is to provide
quick direct access to system features, not to compete with menus
over the number of items they can hold.
That is, we need to get from the current state where each device
appears as a toplevel item, to a state where we have one quick
toggle for each device type plus one for VPN.
The decoupled VPN section still behaves largely as it did as a
subclass, with the notable difference that it no longer uses
a submenu item, so all VPN connections now appear at the toplevel.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
Right now the indicator itself tracks all devices and connections,
creates and destroys the corresponding menu items, matches them
to a section and updates the connection/device arrays that are
attached to the section.
Sounds messy?
It is slightly less effective to connect multiple handlers to the
same NMClient, but let's assume that devices and connections aren't
added/removed at 60 frames/s, and we can add some readabilty by
moving the code into different classes that only have to care about
the bits that are relevant to them.
The VPN section is a good starting point, because its handling is
already quite different from device sections.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
At its core, it's the sort order tracking from NMConnectionItem
with a bit of sugar on top to provide access to the ordered items
and the ability to provide a custom sort function.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
You could argue that the item name is closer to what is displayed
to the user, but it doesn't really matter: Connection items will
always use the connection ID when there is more than one, which
is the only case where sorting matters.
However sorting by items will allow us to generalize the code, and
use it for items that do not represent a connection.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
When not in radio-mode - that is, when the connection is the only
connection for its device - we want the item to represent the
device as a whole.
Achieve this with a small ConnectionItem subclass that adds a
:device-name property for that purpose.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
When not in radio-mode - that is, in the single-connection
case - the item currently uses a plain 'Connect' label.
That is OK while the item is inside a submenu that describes
the device, but we will soon stop using a submenu when a single
item can describe the device as a whole.
Prepare for that by adding an icon that is shown when in non-radio
mode, and include the device name in the label in that case.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2407>
Newer versions of IBus (> 1.5.26) have the IBUS_CAP_OSK capability
which can be used to hint the active IM about an OSK driving input as
opposed to a physical keyboard. This may be used by IMs to tweak their
behavior to suit OSKs better.
Add the GNOME Shell side handling for this capability, and toggle it
on whenever the OSK is visible.
Since this is a far too new enum value and we don't want such new
IBus dependency, this change plays fast and loose with JS guarantees,
since a logical OR with an undefined value results in the other operand
unmodified it will work for older versions where the capability does not
exist and thus we want nothing extra enabled.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2415>
We are calling _loginScreenSessionActivated() even in cases where this is
not required. For example, there are cases where the Active property is
already `true` and properties like `IdleHint`, `IdleSinceHint` and
`IdleSinceHintMonotonic` change. In this cases _loginScreenSessionActivated()
is called and will reset the authPrompt when not necessary.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2411>
We do want to update the best AP on strength change, in case we
can switch to a better one.
But even if we can't and the AP is unchanged, the icon should
be updated to reflect the change.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2414>
Right now DeviceCategory is a small section wrapper that shows
a summary instead of its content if it contains too many items.
It will eventually eventually turn into the base class for
network device quick toggles. Who would have thought that
when it was added for the "there's a computer with 32 ethernet
cards" fringe case?!
For now, give it a more appropriate name and use device types
instead of our made-up categories, now that the two map neatly.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
It's odd to swap out a switch with a status label, and the
information isn't that helpful to begin with: It's either
transient (connecting, deactivating, ...), or too little to
be meaningful (unknown, failed, ...).
We're also perfectly happy to not show it in "label mode"
(i.e. when there's just one VPN).
Just get rid of it.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
Stop providing detailed state descriptions, instead use a name
that best represents the device at the moment (like a wifi SSID,
the carrier name, or the device name as determined by network manager).
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
The _devices property is another case of overloading the
"device" term.
Fun fact:
this._devices[device._delegate.category].devices
uses three different meanings of "device" (section, NM.Device, item).
The devices array in sections won't be around for much longer,
but the property that tracks the sections is worth renaming.
While at it, use a map instead of a plain object, which has a
guaranteed order when iterating (which will come in handy later).
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
Different access points can belong to the same wireless network. As
NetworkManager doesn't handle this for us, we need to track networks
ourselves, and we currently do this using ad-hoc object literals and
monkey-patching.
Clean this up by factoring out a proper WirelessNetwork class, and
associate them to items with a map.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
Interpreting the different flags is better left to the domain
experts at NetworkManager. It is also much more likely that
NM's own functions will handle newly added flags than our own
code.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
There is a straight mapping between running/enabled and visibility,
so bind them instead of using a signal handler.
_syncConnectivity() is called both from _syncMainConnection() and
on connectivity changes, which should cover any running/enabled
changes.
That just leaves updating the icon on state changes.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
We would want the signals to be disconnected if we ever happened to
destroy the indicator. Even if we don't, connectObject() is simply
nicer when connecting half a dozen handlers at once.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
There is only one case where we show a notification: When activating
a connection failed.
There is therefore no reason for a generic wrapper around the
notification API. Likewise, tracking the source is a bit pointless,
given that the notification is transient. In fact, as we destroy
an existing notification *before* checking for the source, any
previous source will be gone by that point.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
The function is a handler for the `notify::state` handler, so
the state and reason parameters used in the checks are always
undefined.
In addition, `DEACTIVATED` is not (just) a failure state. We
clearly don't want to complain about a failed connection when
the change happened on request of the user.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
We never used it, so after more than ten years, it seems safe to
assume that we never will.
Plus different items pass different types, which makes it pretty
much impossible to use, even if we wanted to (which apparently
we don't 🤷️)
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
When deciding on whether to show the 'no-route' icon, we check
for the client's connectivity *and* whether the devices's active
connection is used as primary connection.
This is currently masked by the indicator updating the icon on
connection changes anyway, but items should still notify the
change themselves.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
The wireless device item tracks the active access point in order
to update its icon on signal strength changes.
However we currently don't synchronize the initial state, so we
miss strength changes until the first access-point change.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
Otherwise any unrelated errors in that function are hidden,
because exceptions in async functions are turned into promise
rejections (and JS cannot know that we won't handle it at a later
point).
It wouldn't happen to me of course 😉
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
I wasn't genuiously going to touch those lines, but I ran into
a limitation of the run-eslint script:
It currently bases changed lines on a diff between HEAD and main
instead of the commit-by-commit log.
The two can vary quite a bit when shuffling code around, and those
are the lines the tool kept complaining about.
I'll look into improving the script, but for now it's quicker to
just shut it up by fixing up the complaints.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2406>
There isn't always a serviceName when we set a message. Furthermore we
are passing the serviceName only to decide if we want to wiggle or not.
Pass the wiggle parameters instead. If they are missing, we don't want
to wiggle.
This fixes the following error:
JS ERROR: Exception in callback for signal: login-format-changed: Error: Wrong type number; string expected
setMessage@resource:///org/gnome/shell/gdm/authPrompt.js:542:13
_showRealmLoginHint@resource:///org/gnome/shell/gdm/loginDialog.js:931:26
_emit@resource:///org/gnome/gjs/modules/core/_signals.js:114:47
_updateLoginFormat@resource:///org/gnome/shell/gdm/realmd.js:85:18
_reloadRealm@resource:///org/gnome/shell/gdm/realmd.js:57:14
_onRealmLoaded@resource:///org/gnome/shell/gdm/realmd.js:64:14
_makeProxyWrapper/</<@resource:///org/gnome/gjs/modules/core/overrides/Gio.js:245:34
Fallout from 526f0711f1.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5547
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2404>
Now that the old system menu has been ported over, we can move
the power toggle to its intended place. The main difference to
the stand-alone toggle is that the button now uses its natural
size rather than the fixed size of regular quick items.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2393>
This port is a bit messier than the previous ones, because the
existing menu section translates less directly to the new UI,
which uses a row of individual toggles for settings, lock and
shutdown.
In order to not complicate the grid layout further by supporting
rows with a different number of columns than the overall grid and
children at their natural size, create a custom, non-reactive
SystemItem item that spans an entire row, and contains the individual
toggles.
This works quite well, even with the shutdown item that uses a menu
for the various actions.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2393>
With menu support in place, this is now a straight-forward port:
Just add the existing profiles section to a QuickToggleMenu instead
of a submenu item.
The toggle itself now switches between 'balanced' and the last used
non-default profile.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2393>
Now that the QuickSettingsMenu supports child menus, it is time
to add menu support to quick toggles.
Do that by introducing a QuickSettingsItem parent class with a
construct-only :has-menu property, as that will allow including
menus in items that aren't following the standard icon+label
pattern of QuickToggle (yes, we'll have some of those).
A common parent class also allows us to control the type of
menu that is created. That's important, as we need another
custom menu type to
- constrain the menu's y position to place it underneath
the source actor
- use open/close animations taylored for this use case
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2393>
This is where it gets hairy.
Some quick settings items will have an associated menu. When
opened, the main menu should grow to make room for the child
menu, as it would do if it contained the child.
At the same time, the child menu will attach to its parent, and
block all input there while it is open.
Menu-modal menus so to speak.
Implement this as follows:
- change the QuickSettingsMenu actor to a stack that holds
- the boxpointer (a.k.a. the actual menu)
- an overlay actor to hold child menus
- use constraints to bind the overlay's position and width
to the boxpointer; the y position is offset to align the
overlay with the grid inside the actual menu
- add a placeholder actor to the main grid, whose height is
bound to the overlay height; using a "clone" instead of
the actual overlay means that layout changes are not triggered
from outside the grid hierarchy, which would be prone to
allocation warnings
- update the layout manager to not allocate the placeholder as
a regular grid child, but instead include its height in the
space underneath the row with an open menu
- apply a dim effect to the boxpointer when a child menu is
open, to indicate that input is blocked
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2393>
WorkspaceBackground's allocate is a hot function called every frame
during overview animations. Port it to C.
While we're at it, cache the work area and the monitor geometry, which
do not need to be re-fetched on every allocation.
This reduces the average WorkspaceBackground allocation time from
0.134 ms to 0.017 ms. With four workspaces, scrolling the overview sees
an average WorkspacesView allocation time improvement from
1.104 ms to 0.678 ms.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2394>
The aggregate menu with its submenus isn't well-suited for simple
on-off actions, so we didn't expose the global color-scheme support
that was introduced last cycle.
Quick settings on the other hand are a natural fit for actions like
this, so add a corresponding toggle.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2392>
The power indicator should not be a regular quick toggle, but
instead be part of a "system area" row at the top of the menu.
But as in the end it is still a simple button, we can do the
port to quick settings now, and move it later when the system
row is implemented.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2392>
For now, this is another simple toggle. The icon, state and
visibility reflect what the old menu did, and the top bar
icon is still only shown when devices are currently connected.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2392>
Another simple toggle. Unlike the old menu, it is always shown
if airplane mode is supported, not just while airplane mode is
active.
We still only show the top bar icon while airplane mode is on.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2392>
Night-light is now a simple, always visible toggle that directly
controls the underlying setting. No change to the top bar icon,
which is still only shown while night-light is active (read: at night).
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2392>
Like the aggregate menu currently, the quick settings menu will
eventually contain some items that should dismiss the menu. And
as those items may appear in child menus or otherwise be nested,
a public method on a global object is more convenient than handing
the main menu down the hierarchy.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2392>
The new indicator will eventually expose all the system status
items that are currently provided by the aggregate menu, but in
a more accessible form than the current submenu-heavy menu.
Right now this just adds the new empty indicator to the top bar,
alongside the existing aggregate menu.
We can then move items over one-by-one.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2392>
The quick settings menu is a popover that arranges items in a
reflowing, homogeneous grid. Grid children may span multiple
columns, but not rows.
For now the QuickSettingsMenu that contains the grid is just a
convenience wrapper around the layout manager that does the heavy
lifting. The two will become more intertwined when we add support
for menu toggles though, so the custom menu type is unfortunately
needed.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2392>
Multiple booleans - both in arguments and return values - are almost
always problematic API, because people have to memorize (or more likely
look up) the meaning of each position.
Instead, return a JS object so each value has a name attached to it.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2370>
After porting the more complex cases - in particular those that
affect a module's API - we are left with straight-forward D-Bus
method calls that can be moved to promise-based wrappers in one
go.
For consistency, this also switches from Remote to Async where
the call result is ignored.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2344>
The LoginManager abstraction is still mostly callback-based, not
least because the methods are thin wrappers around logind D-Bus
calls.
However as gjs' dbus wrapper now generates promised-based wrappers
as well, we can implement a proper async API just as naturally.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2344>
Thanks to recent gjs changes - namely the ability to promisify
interface prototypes and promise-based D-Bus wrappers - we can
modernize the ContentTypeDiscoverer API to use an async function
instead of a callback.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2344>
Our search provider API has to be asynchronous to support remote
providers. It doesn't have to be based on callbacks though, now
that async functions provide a nicer alternative.
That is particularly true after gjs's D-Bus wrapper started to
generate promise-based method variants.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2344>
Provider loading has been synchronous since 2013, so we can
just as well return the results directly instead of passing
them to a callback.
Even if we returned to asynchronous loading in the future,
we wouldn't want to use a callback, but make the function
itself async.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2344>
There are a couple of places left where we still use the old
indentation style, update them before making code changes.
After that, there are only a couple of non-type-safe comparisons
left of legacy style, so change those as well while we're at it.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2344>
The latest mockups move the screen sharing indicator into a
separate control, similar to the existing indicator for built-in
screen recordings.
As this removes the submenu and only keeps the top bar icon (for
external screen recordings), this will smooth the transition to
quick settings.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
Location services aren't expected to change frequently: They are
either disabled globally, or permissions are granted on a per-app
basis.
This is less of a concern while the setting is exposed in a small
submenu, but as we moving to quick settings, it does not deserve
the prominence of a quick toggle. Just the top bar icon and Settings'
privacy panel should be enough.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
Other than connecting stream when necessary, the volume menu
only forwards slider events to the indicator, and method calls
from the indicator to the appropriate slider.
Just cut our the middle-man and let the indicator handle the
slider items directly.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
On error, we currently print a warning and bail out early. That
means we don't hide the slider, despite it being non-functional
without the proxy.
Fix this by making sure _sync() is called in any case. While at
it, use an appropriate log level for the warning message.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
Properties are null while a proxy has no owner. Javascript helpfully
coerces that into `Number(null)` (a.k.a. 0), so we end up with a
broken slider.
Explicitly check that the value is an integer before doing the
comparison to catch that case.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
The new class abstracts away the nitty-gritty of bluetooth- and
airplane-mode handling, and exposes just what the UI needs.
This will become more important with quick settings, where there's
a stronger separation between top bar icon and quick toggle.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
There's no good reason for waiting for the proxy to be initialized
to connect signals. In fact, connecting the signal beforehand
ensures that the handler is in place when the proxy fetches the
properties, so we don't have to call the handler explicitly.
That in turn allows us to rely on the signal parameters to only process
changed properties.
To achieve that, construct the proxy manually, and then initialize
it asynchronously in a Promise.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
There's no good reason for waiting for the proxy to be initialized
to connect signals. In fact, connecting the signal beforehand
ensures that the handler is in place when the proxy fetches the
properties, so we don't have to call the handler explicitly.
That in turn will allow us in a follow-up to rely on the signal
parameters to only process changed properties.
To achieve that, construct the proxy manually, and then initialize
it asynchronously in a Promise.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
Don't try to initialize and then enable an extension if it is not supported
in the current mode. Otherwise the extension will not be initialized, and the
initial ERROR state is not cleared. Once it is in ERROR state we can't enable
it anymore when we switch mode. Instead, leave the extension in INITIALIZED
state, so that it will be initialized when appropriate. This happens for
extensions that support the unlock-dialog mode but not the user mode.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2378>