Compare commits

...

236 Commits

Author SHA1 Message Date
9a45d9692a Bump version to 3.35.2
Update NEWS.
2019-12-11 18:51:11 +01:00
4a6c2f1fe6 util: Place spawned processes into a systemd scope
This improves the separation from the shell for applications launched
with Alt+F2 and in a few other cases.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/863
2019-12-11 09:34:36 +01:00
086ba11621 shell-global: Place launched applications into a systemd scope
This improves separation from the shells service scope for applications
launched using an XDG desktop file.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/863
2019-12-11 09:34:36 +01:00
154655838d weather: Only require auto-location authorization if sandboxed
Since commit 87e60ed97843, geoclue no longer pretends that authorization
is useful for system-installed apps (as they can easily lie about their
ID). Unfortunately this broke our auto-location support in case Weather
is installed non-sandboxed, as we are waiting for an authorization that
will never happen.

Unbreak it by only requiring authorization when installed as Flatpak.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1823
2019-12-09 11:10:54 +00:00
0795d8df5f magnifier: Adapt to painting and picking API change
This was left out in 988a0e7314

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/891
2019-12-07 13:08:28 +00:00
85bec783ee screenShield: Adapt to painting and picking API change
This was apparently forgotten in 988a0e7314, causing the warning:
`JS ERROR: Error: Too few arguments to method Clutter.Actor.paint`

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/891
2019-12-07 13:08:28 +00:00
ccd8b47d30 popupMenu: Close when a system modal pops up
Just like switcher popups, popup menus don't play well together with
system modals, and generally have a lower priority. So just like
switcher popups, close popup menus when a system modal dialog pops
up.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1536
2019-12-06 20:26:01 +01:00
c0309d9732 switcherPopup: Dismiss when a system modal dialog opens
As system modal dialogs may open without user interaction (for instance
polkit or network agent requests), it is possible for them to pop up
while the app/window switcher is up.

The current result of having both up simultaneously is clearly broken,
so we can either dismiss the popup or prevent the modal dialog from
opening. Assume that the dialog indicates a more important action and
should therefore take precedence, so go with the former.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1536
2019-12-06 19:55:39 +01:00
0185c288c3 perf-helper: Remove unused atoms
Those aren't used for anything, but make the helper dependent on X11.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/887
2019-12-05 16:51:00 +01:00
3c4c37e4d0 perf-helper: Add content for custom drawing
Drawing windows got a lot more involved with the advent of client-side
decorations. Instead of accounting for visible and invisible borders,
titlebar and shadows when necessary, just add an empty child for the
custom drawing.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/887
2019-12-05 16:51:00 +01:00
2ba4108838 appDisplay: Rename _allItems array to _orderedItems
Since both the `_items` object and the `_allItems` array include the
same items, the difference between those variables seems unclear. The
real difference between them (except the different data type) is that
`_allItems` is ordered in the same order as the visible grid, so rename
`_allItems` to `_orderedItems` which makes that more obvious.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/799
2019-12-05 15:43:34 +00:00
203c3f9949 appDisplay: Make AllViews folderIcons property private
This property is not used anywhere outside the class, make it private.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/799
2019-12-05 15:43:34 +00:00
61b71998a0 appDisplay: Make _items object a Map
Use a Map instead of an Object here makes it easier to loop through
keys, which we're going to do in the next commit.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/799
2019-12-05 15:43:34 +00:00
c4fa052b03 appDisplay: Use _getCategories function instead of duplicating the code
We already have a function which gets the categories of an app and
handles the null-return case, use it.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/799
2019-12-05 15:43:34 +00:00
f3eeb94c90 checkBox: Fix expand and align properties
In f2bd39b20c, the expand property for
checkboxes was accidentally set to the grandchild (the StBin), instead
of the child (the StBoxLayout) of the StButton. Fix that and let the
BoxLayout expand instead of the Bin.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/885
2019-12-05 15:35:22 +00:00
a2044c61ae extensionPrefs: Always redefine getCurrentExtension() on prefsModule access
We have three interactions with an extension's prefs module:
 - we import the module
 - we call its init() hook
 - we call its buildPrefsWidget() hook

The first two are one-time actions where we expect most getCurrentExtension()
calls (local imports, initTranslations() etc.).

However it's still possible that the extension will use the utility function
in buildPrefsWidget() as well, either directly or via other functions like
getSettings(): Make sure getCurrentExtension() returns the correct extension
in that case, not the last one whose preferences were initialized.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/873
2019-12-05 15:24:34 +00:00
2703eed446 extensionPrefs: Simplify state change handling
The new `ExtensionStateChanged` signal already passes the changed
extension object, no need to request it again with `GetExtensionInfo`.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/873
2019-12-05 15:24:34 +00:00
59a43f496d appDisplay: Move to rename folder location
Following the same reasoning of the previous commit, move to
the renamed folder location.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/883
2019-12-05 12:00:11 +00:00
d28bc7afe6 appDisplay: Show newly created folder when creating
The icon grid currently sorts icons by their names. When creating new
folders, the folder may end up being in a different page, and that's
confusing since we don't actually move to where the new folder is.

Move the icon grid to the newly created folder.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/883
2019-12-05 12:00:11 +00:00
22cb0b002d closeDialog: Fix scale of dialog for x11 clients in Wayland sessions
We missed this case in b6e57a5ae8,
XWayland clients obviously don't use MetaWindowWayland and thus they
don't apply the double scaling that commit was meant to fix.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/884
2019-12-04 22:25:03 +00:00
021f3e49b5 keyboard: Update extended key size on parent size changes
Extended keys should have the same size as their parent key, so
make sure to update them when the parent size changes.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1976
2019-12-04 20:48:37 +01:00
91bf7f1e44 keyboard: Use parent key's allocation for extended key size
An outdated allocation is likely still better in this case than the
preferred size, so use that instead of width/height which may fall
back.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1976
2019-12-04 20:48:37 +01:00
7fbdaadce2 keyboard: Create extended keys before updating hover state
We want extended keys to have the same size as their parent key,
but this is currently broken and the keys end up with their
parent key's preferred size (which is smaller than its allocated
size).

This is due to the way ClutterActor's width/height properties work,
which only return the "real" (i.e. allocated) size when the allocation
is valid, and fall back to the preferred size otherwise.

As changing an StWidget's hover state involves adding or removing
the `:hover` pseudo class, this is currently always the case.
Creating the extended keys first means the keyButton's allocation
is probably valid, so do that.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1976
2019-12-04 20:48:37 +01:00
ff7dfa9259 keyboard: Fix widget leak
We currently create an extended-keys popup every time it is requested,
but only destroy it (or rather: the last one) when the keyboard itself
is destroyed.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1976
2019-12-04 20:48:37 +01:00
85f10f1f6a keyboard: Use camelCase
Those aren't GObject properties, so should follow the regular JS style.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1976
2019-12-04 20:48:35 +01:00
9b4780fa1d keyboard: Reindent timeout handlers
We are going to touch some of the code, so take that opportunity
to move them to the new indentation style.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1976
2019-12-04 20:47:09 +01:00
8c4d07ba92 HACKING.md: Update sample code to use paint context
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/827
2019-12-03 19:07:15 +00:00
632a643994 Use paint and pick context to get framebuffer
Mutter and Clutter was changed to pass around the current target
framebuffer via the paint context instead of via the deprecated Cogl
framebuffer stack.

The framebuffer stack has also been removed from Cogl so change to use
the one in the paint context instead.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/827
2019-12-03 19:07:15 +00:00
988a0e7314 Adapt to painting and picking API change
While still leaving them unused, pass around ClutterPaintContext and
ClutterPickContext when painting and picking.

The reason for splitting this change up in two is to make it possible to
bisect easier in between the API change and the change to using the
framebuffer passed around with the temporary contexts.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/827
2019-12-03 19:07:15 +00:00
73776508b3 st: Remove broken parsing of @media rules
This code didn't even pay attention to the
cur_stmt->kind.media_rule->media_list, and unconditonally considered
each statement in the ->ruleset to be of kind ruleset.  That seems
broken.

(The theme doesn't use any @media queries, and they are unsupported
anyway.)

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1979
2019-12-03 18:53:36 +01:00
01c0803a4a Fix always-true condition
CrCascadePrivate->sheets is a statically-sized array inside the
struct; it can't be NULL.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/861#note_659216
2019-12-02 08:04:51 -06:00
582bfe830a cr-rgb: remove handling of "inherit" and "transparent"
st-theme-node.c already handles those by itself.

Part of https://gitlab.gnome.org/GNOME/gnome-shell/issues/1934
2019-11-29 17:50:03 +00:00
236bdaa53c Handle "color: inherit" directly in get_color_from_term(), not in libcroco
The idea is to move handling of "inherit" as early in the parsing as possible.

Part of https://gitlab.gnome.org/GNOME/gnome-shell/issues/1934
2019-11-29 17:50:03 +00:00
52f5793c9b Use get_color_from_term() instead of get_background_color_from_term()
The former already checks for term_is_transparent() as its first
thing.

Part of https://gitlab.gnome.org/GNOME/gnome-shell/issues/1934
2019-11-29 17:50:03 +00:00
1e8e08ce61 Simplify if statement
And make the end of st_theme_node_lookup_length() consistent with
st_theme_node_lookup_color() and st_theme_node_lookup_shadow().

Part of https://gitlab.gnome.org/GNOME/gnome-shell/issues/1934
2019-11-29 17:50:03 +00:00
05c3ac2359 get_length_internal() - remove unused argument 'suffixed'
It was passed as NULL in the single caller of this function.

Part of https://gitlab.gnome.org/GNOME/gnome-shell/issues/1934
2019-11-29 17:50:03 +00:00
47758d16ff Include the libcroco sources directly under src/st/croco
This is all of the original libcroco, minus these two which we don't use:

  - cr-sel-eng - the CSS selection engine for xmlNode.
  - cr-style.

Part of https://gitlab.gnome.org/GNOME/gnome-shell/issues/1934
2019-11-29 17:50:03 +00:00
867cffaf20 switcherPopup: Fix scrollable check
When commit c6cea277e replaced Shell.GenericContainer, the check
whether the required width exceeds the avilable width was changed
from using the minimum widths of items to the natural width of the
scroll view.

That doesn't work correctly, as the *natural* width may well exceed
the actually used width: SwitcherList bases its width request on
children's minimum sizes to force labels to ellipsize.

Fix this by using the minimum width of the scroll view's child instead.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1834
2019-11-29 16:47:48 +01:00
5f9036e815 calendar-server: Use correct timezone for all-day events
Since commit 28c535e34, we use the timezone associated with the ICalTime
instead of the default timezone when converting to time_t. However while
that is correct for most events, for ICalTimes that don't have a timezone
associated we still want to fall back to the default timezone instead of
UTC.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1895
2019-11-28 01:42:50 +01:00
5b957f69d8 theme: Add light styling to message buttons
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/865
2019-11-27 19:11:24 +00:00
749a4c9f6c appIcon: Draw running dot above the overview icon
Prevent the app-well-app-running dot from getting unintentionally
hidden behind the overview-icon background by initializing the
running-dot after its sibling overview-icon.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/866
2019-11-27 02:59:13 +01:00
0a9e1b4173 fileUtils: Delete deleteGFile hack
It is true that delete is a javascript keyword, but that doesn't
prevent it from being used as method name - there are event built-in
types like Map or Set with delete() methods!

So if that hack was ever needed, this hasn't been the case for years
now; just removed the hack now.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/862
2019-11-26 22:17:28 +00:00
66f4feeb16 Update Brazilian Portuguese translation 2019-11-26 19:58:38 +00:00
e642e1c106 texture-cache: Remove also scaled keys from the cache
We're storing in the texture cache images and scaled images appending
the scaling factor to the key. When a file changes the cache key
corresponding to that file is removed, but not the keys for the scaled
ones so that images in the cache are never reloaded.

This patch removes all keys from the cache related to the file that
changes, including those with the scaling factor.

A new set (hash table) was added to keep track of scale used to be able
to remove all possible images in the cache.

When the KEY is removed from the cache, we can look now in the scale set
for and each scale we also remove the key "KEY1.000000", "KEY2.000000",
etc.

Assuming that the number of used scales is small (I would typically
expect one or two), the overhead should be negligible.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/567
2019-11-26 08:28:21 +01:00
d9ef612302 data: Enable clean session shutdown after gnome-shell failure
If the GNOME shell crashes, we run a service that may disable
extensions. This is important so that users will not be locked out of
their own session in case an extension is causing crashes.

As this is a very agressive action, we tried to only do this in the
first two minutes of the session. Unfortunately, the logic was broken
and would result in an unclean session shutdown.

Fix this by using the newly introduced gnome-shell-disable-extensions
file. This is created by the extension subsystem for a period of time to
indicate the extensions may be the cause of a gnome-shell failure.

See
  https://gitlab.gnome.org/GNOME/gnome-session/issues/43
for a log of the bug happening and the gnome-session part to fix this.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/858
2019-11-25 21:49:49 +00:00
f742484795 extensionSystem: Create a file to flag that extensions are being loaded
When the extension system is loaded, create the
gnome-shell-disable-extensions file in the users runtime directory. This
file is automatically removed 60s later. The sole purpose of this file
is to be consumed by the systemd units. If the file exists, the systemd
units will disable extensions when the gnome-shell fails.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/858
2019-11-25 21:49:49 +00:00
1ecdb393d7 Update Greek translation 2019-11-25 19:20:20 +00:00
eee1ab4890 introspect: Fix whitelist check
The whitelist is a list of well-known D-Bus names, which we then search
for the unique name we get from the method invocation - unsuccesfully.

Fix this by watching the bus for any name in the whitelist in order
to maintain a map from wel-known to unique name that we can use for
matching.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1916
2019-11-25 20:00:10 +01:00
42eb9f4a28 theme: Add :active styling to message-close and media control buttons
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/855
2019-11-25 18:02:07 +02:00
18421e8aed theme: Add message close button styling
Since the notification message close button had no border, or mouse
over effect, there was no way to determine whether the mouse cursor
were over the button.

Improve this by adding a message-close-button class for the close
button, and a styling for its hovered state, based on media control
button styling.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/855
2019-11-25 18:02:07 +02:00
c255b4d14e theme: Darken hovered message-media-control button
Increases contrast between normal and hovered states in
message-media-control buttons. Previously there was very little
difference between the two states, making it hard to distinguish
whether the mouse cursor was over the button.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/855
2019-11-25 15:52:59 +00:00
bb48205aae extensions-tool: Fix removing from settings list
When removing a string from a settings list, we iterate over all
existing entries and copy all strings except the one that's being
removed to a new list, which is then written to GSettings.

However we currently always increment the index, so we end up with
a NULL entry in place of the removed entry, which is then interpreted
as the end of the list. In other words, we also remove all entries
that follow the removed string.

Fix this by looping over the list entries instead of the index, and
only increment the index for entries we copy.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1946
2019-11-25 16:18:08 +01:00
5a287a4205 appDisplay: Add a timeout when switching pages during DnD
Currently when dragging an icon to the space above or below the appGrid
to switch pages, we do so very quickly without checking when the last
page-switch happened. This makes it hard to move icons to pages which
are not the first or the last one, since the other pages are skipped
very quickly.

To fix this, add a timeout of 1 second that blocks switching pages after
a page-switch using drag overshoot occured.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1693
2019-11-24 19:35:30 +01:00
b0c8192496 appDisplay: Add threshold after overshoot page switches
We currently always switch app pages when a dragged app icon
moves outside the grid boundaries, regardless of any previous
page switches. This makes it too easy to switch multiple pages
accidentally, so add a small threshold that the icon has to
move back towards the grid before allowing another page switch.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1693
2019-11-23 20:11:21 +01:00
0897915b05 appDisplay: Simplify event blocking while folder is opened
There's no need for a `inhibitEventBlocker` interface. Since we connect
to "open-state-changed" of our folders in the AllView anyway, we can
just make the event blocker visible while a folder is opened, and hide
the event blocker during DnD.

This allows keeping the eventBlocker reactive at all times and fixes an
issue where DnD to create a new folder is impossible if no folders are
present because the eventBlocker would not get inhibited.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1652
2019-11-23 18:31:46 +01:00
2894085c45 volume: Skip volume-change feedback while playing
The audio feedback for volume changes is useful when nothing is outputting
sound, but only then. Skip the sound notification in that case.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/53
2019-11-23 15:16:51 +01:00
c506eda20a gvc: Update submodule
gnome-volume-control now has API to expose the stream state, which
we will need in the following commit.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/53
2019-11-23 15:16:51 +01:00
a8005e3c30 slider: Stop emulating drags in key handling
Emitting ::drag-end after changing the slider value via arrow keys
was a cheap way to make the sound feedback work for keyboard input.
But now that the volume indicator plays the sound on ::value-changed
as well, we can stop doing that - after all, key presses aren't drags.

Besides that, this will make the limiting of feedback to actual volume
changes from the previous commit work for key events as well.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/53
2019-11-23 15:08:54 +01:00
6c8eb1a18e volume: Only emit sound feedback after volume changes
gnome-settings-daemon doesn't play the volume change sound when
the volume stayed the same (that is, it is already at its maximum
or minimum). This looks like the right thing to do, so copy its
behavior.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/53
2019-11-23 15:08:54 +01:00
5af8bf2788 volume: Add back sound feedback on scroll
Commit 8d4855f100 accidentally removed the volume change feedback
for scroll events. Add it back to be consistent again with moving
the slider via arrow keys, slider drags/clicks and gsd's media keys
handling.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/53
2019-11-23 15:08:54 +01:00
7e9f30da0a appDisplay: Ensure we don't recreate existing AppIcons for folders
This was missed in 910037f014, make sure
we do the same thing for AppIcons that are created when reloading
folders.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/851
2019-11-23 22:01:09 +01:00
2842670082 cleanup: Remove another pair of unneeded parentheses
Eslint didn't spot this before version 6.5, so this fell through
the cracks.
2019-11-23 01:29:20 +01:00
95f388b9a7 dateMenu: Don't ellipsize forecast times and temps
While those should be concise enough to fit, they may not where
temperatures drop into double-digit negatives. It seems better
to accept some awkward horizontal scrolling in that case than
shorten relevant information.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1926
2019-11-23 01:13:08 +01:00
e72c38b5ab dateMenu: Move weather forecast validity check
Commit b779f6f728 added a check to filter out invalid weather forecasts.

However the check is currently done when creating UI for the forecasts,
which means we end up with fewer forecasts than we could display if any
forecasts are invalid.

We can avoid that issue by checking the validity while collecting the
forecasts, so do that instead.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1927
2019-11-23 01:13:08 +01:00
f6f373b0c2 dateMenu: Only show forecasts
We currently always start with the current weather info, then append
forecasts. This is slightly confusing, as the only hint that the
first item is special is the past (and potentially "odd") time.

Stop doing that and base all items on forecasts.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1927
2019-11-23 01:13:05 +01:00
b757f5c655 dateMenu: Don't limit weather forecasts to the same day
As we get closer to midnight, we show fewer forecasts than we could
fit, or none at all. It makes more sense to continue the forecasts
into the wee hours in that case, so only exclude past forecasts,
but not ones from following days.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1927
2019-11-23 01:13:05 +01:00
784c0b7e4b dateMenu: Try harder finding a reasonable weather location name
Weather stations can have unwieldy long names, which don't fit the
limited space we have available. City names are usually more suitable,
so use the name of the nearest city instead if possible.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1927
2019-11-23 01:13:05 +01:00
f2df9f1ae4 dateMenu: Add some spacing between weather header and location
In case of a very long location name, the label may take up all
available space. Make sure there is at least some spacing between
header and location in that case.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1927
2019-11-23 01:13:05 +01:00
18a1435c25 dateMenu: Bottom-align weather title/location
The two labels use different font sizes, so they don't align properly.
Unfortunately we don't have BASELINE alignment in Clutter, but at least
END comes closer than the default FILL.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1927
2019-11-23 01:13:05 +01:00
669d12f957 dateMenu: Re-indent weather section
Before making code changes, make sure the class confirms to the
new coding style.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1927
2019-11-23 01:13:05 +01:00
d52b23dec3 switcherPopup: Improve modifier-less keybinding navigation
Commit c899453800 lifted the requirement of switcher keybindings
to contain a modifier, however it is currently only possible to
finish it by letting it time out.

Improve that by also accepting space/enter key presses to confirm the
selection immediately.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1883
2019-11-22 23:46:31 +00:00
998fe58482 switcherPopup: Use roundtrip time when the popup is modifier-less
The noModsTimeout obviously finishes inside a timeout callback, which
means `global.get_current_time()` might return Clutter.CURRENT_TIME (ie.
0) when called inside it, because it's not called while handling an
event. This means when switching apps or activating a window, the
timestamp passed to `activate_window` may be 0, which is the reason why
the altTab switcher is currently broken when using modifier-less
keybindings.

Fix that by using `meta_display_get_current_time_roundtrip`, which
always return a valid timestamp, instead of
`shell_global_get_current_time`.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/847
2019-11-22 22:09:47 +00:00
109f39afa5 pageIndicators: Redesign and add position-based animation
Remove setCurrentPage() function, introduce setCurrentPosition() instead,
which allows to have fractional positions.

Make inactive dots smaller, filled and partially transparent, as opposed to
larger and fully opaque active dot. Make dots smaller overall, remove
borders. Interpolate each dot between active and inactive state based on
scroll position.

Make it impossible to "uncheck" the active dot.

Thanks Florian Müllner for parts of the code.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1932

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/843
2019-11-23 03:01:51 +05:00
9790b0ee5d st/button: Notify :pressed changes
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/846
2019-11-22 18:55:40 +01:00
c48330a986 cleanup: Use g_clear_handle_id() for g_source_remove()
It makes sure we do not forget to zero the id and lets us avoid
zero checks before. We use it for all new code, lets clean up the
existing code base.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/845
2019-11-22 01:45:25 +01:00
9132063b87 switcherPopup: Show immediately on second key press
We slightly delay showing the switcher popup to avoid flashing it
briefly in the common case of quickly switching back-and-forth
between two windows.

However some users perceive this delay as slowness. Address this by
showing the popup immediately when another key press is consumed
(that is, a key like Tab is pressed).

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1928
2019-11-22 00:20:29 +01:00
d5e8f8cdf7 mpris: Use a scope specific message instead of a global one
Since the mpris implementation of the notification tray supports showing
multiple notification (one for each player), it doesn't make sense to
have only one global property to store the message, since that only
allows referencing one message at a time.

Instead, handle the MediaMessages completely inside the scope of
`_addPlayer()`, this should allow showing more than one message again,
which broke with commit 4dc44304df [1].

[1] https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/791

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/833
2019-11-21 22:54:07 +00:00
55362aed3d messageList: Don't include message actor in error message
Because the message actor could also be undefined or a already
deallocated ClutterActor, we sometimes fail to show the error message
and get an error from Gjs instead.

So make sure we always log the proper error message and just leave out
the actor.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/833
2019-11-21 22:54:07 +00:00
135d178d08 cleanup: Use g_clear_signal_handler() where possible
`g_clear_signal_handler()` is usually cleaner and saver than
`g_signal_handler_disconnect()`. We use it new code, lets also
adopt the existing one.

See also https://gitlab.gnome.org/GNOME/mutter/merge_requests/868
and https://gitlab.gnome.org/GNOME/mutter/merge_requests/940

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/842
2019-11-21 22:37:37 +00:00
e7b9bd75d8 appIcon: Remove drag monitor on destroy
It may happen that the app icon is destroyed with a drag
monitor still around, in which case, a load of warnings
will be shown.

Make sure to remove any pending drag monitor on destroy.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/841
2019-11-21 22:28:46 +00:00
bd173ac5d2 folderView: Reset schemas before removing the folder
When removing the last icon of a folder, FolderView first removes
the folder from org.gnome.desktop.app-folders.folder-children, then
proceeds to reset all its keys, which removes the relocatable schema.

That order of operations turns out to be problematic. Removing the
folder from 'folder-children' destroys the folder icon, which in turn
destroys the folder view, which throws a load of warnings in the
journal.

Fix that by removing the folder after resetting the schema keys. In
fact, what we're doing here is not using 'this' anymore.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/841
2019-11-21 22:28:46 +00:00
bfc7c1cd65 baseAppView: Destroy icon when removing
We cannot rely on the garbage collector to do that in a timely
manner, so destroy it explicitly.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/841
2019-11-21 22:28:46 +00:00
cae69b3a88 allView: Rename variable
The variable that holds the list of application icons is
called 'newApps', but that technically was never true,
since we only create new app icons when necessary.

Rename it to 'appIcons'.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/841
2019-11-21 22:28:46 +00:00
910037f014 allView, frequentView: Only create icons when necessary
The views (AllView and FrequentView) build a list of all applications
they contain. BaseView then diffs between what's currently added, and
what needs to be added, and removed.

This approach has a problem though: creating an AppIcon or a FolderIcon
connects to various signals, and we confuse the garbage collector.

When building the list of applications, instead of always creating new
icons, try to use already existing icons first.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1610
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1694
2019-11-21 22:28:46 +00:00
acaa9f7f77 polkitAgent: Fix spinner
Commit 6af25b282c accidentally changed the case of the property.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/840
2019-11-21 22:04:39 +01:00
b779f6f728 dateMenu: Skip weather forecast if not valid
GWeather.Info.get_value_update() may indicate that the forecast is not
valid, or it may return a timestamp of 0 to indicate the information has
never been updated. In both of these cases, skip creating a widget for
it, as the information will not be accurate.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/835
2019-11-20 13:08:22 -08:00
83f224e08b dateMenu: Format weather forecast times without AM/PM
If the clock is set to 12h, the AM/PM in the weather forecast times
should be clear from the context, because they are the immediately
following hours. This makes it less likely that the times will be
ellipsized (in which case the AM/PM wouldn't be shown anyway.)

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/835
2019-11-20 13:08:06 -08:00
c1a7c71549 Increase .calendar-today visibility
Adds some needed contrast to the calendar widget current day, solving #1873.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/823
2019-11-20 12:33:44 +00:00
c68bd33432 appMenu: Hide stopped spinner actor
Get rid of leftover empty space from the application menu panel
button, that was used by the spinner actor, which remained visible
even after the spinner had stopped.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/1679
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/832
2019-11-19 20:56:16 +02:00
8f4e91a738 animation: Add parameter for hiding stopped Spinner actor
Not hiding leaves the empty actor space visible, which may have an
undesirable effect on the parent element's size or spacing/padding.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/832
2019-11-19 19:55:28 +02:00
6af25b282c animation: Turn Spinner animate parameter into Params option
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/832
2019-11-19 19:54:13 +02:00
e3e1a27f2d Update Friulian translation 2019-11-18 08:56:08 +00:00
c1ec7b2ffa keyboard: Try harder to find a matching layout
While we support a reasonable list of layouts nowadays, we don't
include many variants like `fr+oss`. Instead of directly falling
back to the `us` layout, try stripping the variant first, as the
base layout is likely closer to the expectation than `us`.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1907
2019-11-16 16:12:06 +01:00
45ebb94b33 polkitAgent: Cancel session after disconnecting signal handlers
When cancelling the PolkitAgent session before disconnecting the signal
handlers, we receive a "completed" signal where `gained_authorization`
is set to FALSE, which means we show an error message inside
`_onSessionCompleted()`.

This in turn means we show an error message every time we cancel a
session. In practice this wasn't really relevant so far since we only
destroyed the session when an actual error occurred before. Now that the
dialog supports empty passwords, we also call `_destroySession()` when
the user changes and no longer has a password set, and in this case we
want to cancel the current session without showing an error message.

So to fix this, disconnect the signal handlers before cancelling the
session, which makes sure we don't receive the last "completed" signal
in case we cancelled the session ourselves. This change also allows
removing `this._wasDismissed`.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/829
2019-11-16 12:09:25 +01:00
89bf360bad polkitAgent: Use dialog as confirmation when the user has no password
When a user has no password and a polkit authentication is started,
instead of blindly initiating the admin session, show the regular
"Authentication Requested" dialog (but without the password entry). This
makes sure that the user's admin session is only effectively started
after the user chooses to proceed with the authentication, which
provides an extra confirmation step that can be vital for critical
tasks.

To do this, we show the dialog inside `_onUserChanged()` right after the
dialog was created instead of calling `performAuthentication()` from
`_onInitiate()`. The bug mentioned in `_onInitiate()` is no longer an
issue since we show the dialog in all cases now anyway.

Ideally we should use a different wording than "authentication" when the
user has no password set, and use "confirmation" instead. However polkit
already sends the requests with such messages (e.g. "Authentication is
required to configure software repositories"), and it's important to
show those to the user, so this patch keeps the regular wording.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/829
2019-11-16 12:09:25 +01:00
3a7228cf2f polkitAgent: Only reset UI on session resets while opened
Since `_destroySession()` is not only called before we try to initiate a
new authentication session with Polkit, but also when the dialog is
closed, it's currently possible that key focus is grabbed by the close
button after the dialog was dismissed and hidden. This is causing a bug
where after dismissing one of multiple queued dialogs, key focus goes
away and keyboard navigation with the new dialog is impossible.

Fix this by only resetting the UI of the dialog if the dialog is still
opened/visible at that point.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/828
2019-11-15 23:26:47 +00:00
c1ae634174 panel: Don't chain up to non-existent parent vfunc
Just as with c35b4cede5, there's no
default vfunc implemented by any parent which causes gjs to crash when
trying to call it.

So return EVENT_STOP if the key press successfully toggled the button,
and EVENT_PROPAGATE otherwise.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/830
2019-11-14 19:52:34 +01:00
88bcaafe86 Stop referring to ClutterTexture
ClutterTexture is removed from mutter's clutter fork, so lets stop
referring to it. This also happens to fix an incorrect type cast.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/822
2019-11-12 22:05:13 +01:00
66fc5c07bb background: Add exception to no-loop-func rule
Modifying variables from an outer scope in functions created in a loop
is considered problematic by eslint, because the variable value in the
resulting closure is often not what the coder intended.

In this particular case however, the scoping is correct, so add a comment
to disable the rule locally.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/818
2019-11-11 23:51:17 +00:00
a32c4f30d1 style: Allow lonely ifs where appropriate
We now have a lint rule to disallow lonely ifs, however there are
cases where the "lonely" part mirrors code from the preceding if
clause. Opt out of the lint rule in those cases.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/818
2019-11-11 23:51:17 +00:00
65c5cfd4dc lint: Disable eqeqeq in legacy configuration
Using type-safe comparisons is a good idea, but after not doing so
for a decade, there's too much existing code around for flipping
the switch right away.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/818
2019-11-11 23:51:17 +00:00
0483c78dd1 lint: Sync configuration with gjs
gjs updates its configuration to a much more complete and
thorough set, follow suite.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/818
2019-11-11 23:51:17 +00:00
abc7cc9a26 lint: Convert eslint JSON to YAML
gjs has changed its configuration to YAML, so switch to that format
to keep syncing possible.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/818
2019-11-11 23:51:17 +00:00
913990b9ea folderView: Center folder icon
The FolderView class is responsible for creating the 4-item
grid of the folder icon, with the preview of the first four
apps inside the folder.

However, with the deprecation of StAlign as child properties,
the folder icon stopped being horizontally centralized.

Center the folder icon horizontally again.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/817
2019-11-11 18:12:32 -03:00
61210fdae1 cleanup: Use JSDoc for documentation comments
It's a better fit than gtk-doc, and eslint can validate that they
are complete and use correct syntax.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
077d8f33fb cleanup: Don't use gtk-doc syntax for regular comments
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
e44adb92cf cleanup: Avoid unnecessary parentheses
Extra parentheses usually add noise rather than clarity, so avoid
them.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
ebf77748a8 cleanup: Require "dangling" commas
Since ES5, trailing commas in arrays and object literals are valid.
We generally haven't used them so far, but they are actually a good
idea, as they make additions and removals in diffs much cleaner.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
07cc84f632 cleanup: Only omit braces for single-line blocks
Braces can be avoided when a block consists of a single statement,
but readability suffers when the statement spans more than a single
line.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
c860409da5 cleanup: Use object shorthand where possible
ES6 allows to omit property names where they match the name of the
assigned variable, which makes code less redunant and thus cleaner.
We will soon enforce that in our eslint rules, so make sure we use
the shorthand wherever possible.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
9eaa0089d0 cleanup: Fix missing/stray spaces
Those are wrong according to our style guidelines, but the previous
eslint ruleset didn't catch them.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
682bd7e97c cleanup: Don't shadow variables
Having variables that share the same name in overlapping scopes is
confusing and error-prone, and is best avoided.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
2e4e2500dd cleanup: Avoid "lonely" ifs where it makes sense
If an else block only contains an if statement, the two can be
combined into an if-else block, which cuts down on indentation
and usually helps legibility.

There are exceptions (for instance where the outer if and else
blocks are mirrored), but where it makes sense, change the code
to avoid lonely ifs.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
67ea424525 cleanup: Avoid unnecessary braces
Our coding style has always been to avoid braces when all blocks
are single-lines.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
69f63dc94f ctrlAltTab: Use arrow function for callback
This was left over because we are binding a different `this`, but
it is easy enough to replace.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
bef5043135 jsParse: Unnest functions
Nesting functions can be helpful for private helper functions, but
here they are accessing some variables from the outer scope and
shadowing others. Split them out to avoid any ambiguity.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/805
2019-11-11 19:25:14 +00:00
fea5ecc9e8 allView: Ensure event blocker is reactive before popup is open
The event blocker in AllView is responsible for stealing click events
from the icon grid and closing the folder popup. The event blocker is
kept unreactive when no folder popup is visible, and it's made reactive
as a response to the 'open-state-changed' signal.

Using this signal, though, is problematic. When opening an app folder,
the icon grid first opens space for the folder to fit in; during this
period, it's possible to click on another folder icon, and break the
icon grid state.

Make sure the event blocker is reactive immediately after clicking on
a folder icon.

Related: https://gitlab.gnome.org/GNOME/gnome-shell/issues/1470

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/816
2019-11-11 16:59:45 +00:00
697912d8a4 js: Fix alignment
The old StBin alignment properties defaulted to MIDDLE, while the
ClutterActor properties use FILL. Fix the resulting fallout by
setting the alignment explicitly where necessary.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/809
2019-11-11 16:52:10 +00:00
a0b0237689 Update Indonesian translation 2019-11-10 02:35:26 +00:00
8eb5d5aac5 style: Don't specify font-family
Now that the default font follows the interface setting, stop
overriding it in the CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=688288
2019-11-08 23:48:42 +00:00
f28f041a95 theme-context: Use interface font instead of hardcoded default
With this, gnome-shell will now follow the interface setting instead
of hardcoding a default font, unless when overwritten by the CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=688288
2019-11-08 23:48:42 +00:00
cd84fa824f st: Add Settings:font-name property
This maps to the corresponding interface setting and will be used to
replace the hard-coded font in the CSS.

https://bugzilla.gnome.org/show_bug.cgi?id=688288
2019-11-08 23:48:42 +00:00
40bd65c9ba st: Fix a minor leak
StSetting is used as a singleton, so this leak does not matter in
practise.

Spotted by Jonas Dreßler.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/486
2019-11-08 23:48:42 +00:00
5e43f282a1 calendar: Define EventSourceBase and extend EventSource's
Objects implementing EventSource should have some mandatory methods and
properties, we can ensure this by defining an EventSourceBase abstract
class.

So inherit EmptyEventSource and DBusEventSource from it making sure that
they implement all the needed methods, using native properties and
replacing the 'notify::*' fake signal emissions with proper object
notifications.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/563
2019-11-08 23:12:47 +00:00
d83d8f2c45 modemManager: Define ModemBase GObject class for modems
Use GObject based objects for ModemGsm, ModemCdma and BroadbandModem.
This allows to define a base class that we can use to natively define
properties and notify property changes.

We can now remove the "fake" notify signals with proper properties
notifications.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/563
2019-11-08 23:12:47 +00:00
348e4ac901 background: Inherit Animation from GnomeDesktop.BGSlideShow
Animation background is just wrapping a native GnomeDesktop BGSlideShow
object, so instead of using composition we can now just inherit from the
native GObject, re-using native properties when possible, and avoiding
to keep an extra wrapper to the bg file.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/563
2019-11-08 23:12:47 +00:00
5944a1e74b keyring: Inherit KeyringPrompter from Gcr.SystemPrompter
Keyring is just a simple wrapper to a Gcr.SystemPrompter object, so use
inheritance instead.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/563
2019-11-08 23:12:47 +00:00
0ed702d1af polkitAgent: Inherit AuthenticationAgent from Shell.PolkitAuthenticationAgent
AuthenticationAgent is just a wrapper of Shell's PolkitAuthenticationAgent, so
instead of using composition we can simply extend the base GObject.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/563
2019-11-08 23:12:47 +00:00
fc71f8b33a windowManager: Complete interrupted size change effects
Resizing effects are more finicky as other effects, as the actual
animation is delayed until we receive the ::size-changed signal.

However that signal may never be emitted if the window is destroyed
just after starting the size-change effect, in which case the effect
is never completed, blocking mutter from destroying the corresponding
window actor.

Address this by tracking when a resize effect is pending, and complete
the effect when appropriate.

https://gitlab.gnome.org/GNOME/mutter/issues/655
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/815
2019-11-08 18:58:55 +01:00
cb7374b1ec windowManager: Use Sets to track ongoing effects
We only care whether an effect is ongoing for an actor, not about
any particular order. Sets are more convenient than arrays in that
case, so use them instead.

https://gitlab.gnome.org/GNOME/mutter/issues/655
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/815
2019-11-08 18:58:48 +01:00
f5996a9232 inputMethod: Compare ibus context before processing key event result
In the xwayland-on-demand scenario, it may happen that Xwayland is
shutdown (causing a restart of ibus-daemon to drop ibus-x11) while
we are typing.

If we have a bit of bad luck, this will cause the IBusInputContext
to be disposed (due to its bus "closing") at a time when we have
an ibus_input_context_process_key_event_async() request on the fly.

As the object is disposed in between this would tickle JS (rightfully
complaining that it's been disposed under its feet) and make us pass
an actually NULL IBusInputContext to the corresponding _finish()
function (despite the IBusInputContext being still held alive by some
other refs). This will assert and abort in
ibus_input_context_process_key_event_async_finish() then.

To handle this, listen for IBusInputContext::destroy, and reset our
internal state, this way we can compare on the JS side that the
IBusInputContext is indeed an up-to-date one.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/813
2019-11-08 12:23:15 +00:00
5fd52e99d3 power: Handle "100% but charging" case
I've observed that UPower can occasionally report a charge level of 100%
while the state is still "charging". This usually doesn't last very long
but it is noticeable because the power icon changes to a "missing icon"
icon. This will handle that rare case correctly.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/814
2019-11-07 12:58:54 -08:00
fd5989e99a ci: Fix checking out mutter on stable branches
For stable branches, we currently only check out the correct mutter
branch for merge requests. For the regular pipeline, our code to
determine the current shell branch fails because CI runs on a
temporary "pipeline/12345" branch that doesn't exist for mutter.

Switching to the correct gitlab environment variable fixes that.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/811
2019-11-06 22:52:38 +00:00
cf6beee9e2 screenshot: Allow saving to clipboard
If no target file is specified (i.e. filename is an empty string), the
screenshot will be stored on the clipboard instead.

https://gitlab.gnome.org/GNOME/mutter/issues/789
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/810
2019-11-06 22:45:22 +00:00
be5f5ec9d4 shell: Make screenshot API stream based
Instead of dealing with filenames, make the low-level API use streams
so the target remains generic.

This so far means JS code now determines the appropriate filename to
use for storing the screenshot, but will be used in other ways in
future commits.

https://gitlab.gnome.org/GNOME/mutter/issues/789
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/810
2019-11-06 22:45:22 +00:00
a58bdbfbd4 st: Add StClipboard method to set arbitrary clipboard content
This complements the current text-based API.

https://gitlab.gnome.org/GNOME/mutter/issues/789
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/810
2019-11-06 22:45:22 +00:00
f51952f5d6 shell: Remove format_date() utility function
It is now unused.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/807
2019-11-06 20:19:57 +00:00
ac1f896107 environment: Reimplement Date.toLocaleFormat() override
Now that we no longer go through GTimeVal to convert from Date to
GDateTime, there is no more reason for using a C helper function.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/807
2019-11-06 20:19:57 +00:00
3913fa5044 environment: Stop adding child_set() to layout managers
We no longer use any layout managers that use custom child properties
instead of the generic Clutter.Actor properties, so this override
is completely unused.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/808
2019-11-06 09:42:57 +01:00
32185c17d0 environment: Stop monkey-patching Clutter.TableLayout
It's not used for anything anymore, and may well end up being removed.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/808
2019-11-06 09:42:57 +01:00
d3d165243c cleanup: Use non-deprecated key symbols
Clutter originally cluttered its namespace with key symbols, before
prefixing all symbols with KEY. We still use the unprefixed symbols
occasionally, replace them so mutter can drop the deprecated symbols.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/808
2019-11-06 09:42:57 +01:00
1e203f4631 cleanup: Replace deprecated lower/raise calls
Those methods have been deprecated for a long time, so
move to the drop-in replacement.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/808
2019-11-06 09:42:57 +01:00
0617be9fb9 windowManager: Stop using Clutter.Actor.prototype.reparent()
It has been deprecated for ages, and is about to be dropped from mutter.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/808
2019-11-06 09:42:57 +01:00
0749ac27ce calendar: Use Clutter.GridLayout
Clutter.TableLayout has been deprecated, so move to the recommended
replacement.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/808
2019-11-05 23:50:06 +01:00
d5eafbad87 polkitAgent: Use a timeout for resetting the dialog
Since polkit takes a few milliseconds from initiating the session to
emitting the "request" signal, don't introduce visual distraction by
hiding the password entry and showing it again a few ms later.

So start a timeout of 200 ms when we destroy a session and if no session
request (i.e. a request for a password-authentication) happened during
this timeout, hide the password entry.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/788
2019-11-05 18:52:33 +01:00
5c7a701a68 polkitAgent: Reset dialog to defaults after cancelling polkit session
Since we don't know if polkit/PAM will request a password (emitting the
"request" signal) or use another authentication method like a
fingerprint after the current authentication failed, hide the password
field and make the "Authenticate" button insensitive after cancelling
the session, just like we do when creating the dialog.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/788
2019-11-05 18:52:33 +01:00
cd36301d2b polkitAgent: Make authenticate button insensitive if password is empty
According to the mockups, make the polkit dialogs "Authenticate" button
insensitive and don't respond to pressing the enter key if no password
is supplied.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/788
2019-11-05 18:52:33 +01:00
70203b58ca polkitAgent: Only set key focus to password entry after opening dialog
Set the key focus to the password field only after we got a request
(and therefore know that a password is requested) instead of using
`setInitialKeyFocus()`. This way we don't try to focus the password
field by default if we aren't showing it (e.g. in case the user has no
password or is using fingerprint login).

Also we have to move the call to `grab_key_focus()` to happen after
`_ensureOpen()`, because otherwise the ModalDialog will set the focus to
one of the buttons while opening itself.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/788
2019-11-05 18:52:33 +01:00
c627d47019 polkitAgent: Also show user avatar for root user
Show the user avatar for all users, including the root user. The root
user will always have the generic avatar, but it looks more consistent
than showing no avatar at all.

This way we also don't have to worry about the spacing introduced by the
polkit-dialog-user-layout CSS class, which would give the
"Administrator" label a small offset to the left.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/788
2019-11-05 18:52:31 +01:00
f546715cc3 polkitAgent: Update user name on user changes
Right now we only update the user avatar on the user-changed signal, but
since we also display the users real name we should also update that if
the user changes.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/788
2019-11-05 18:52:15 +01:00
f5e179f03d polkitAgent: Fix a typo of a signal name
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/788
2019-11-05 18:49:05 +01:00
35a265a183 Updated Spanish translation 2019-11-05 15:38:42 +01:00
147a743d8d system: Replace action icons with regular menu items
Besides making the menu a bit less special, it allows us to fit both
shutdown and suspend actions without any hidden alt-key Easter eggs.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/270
2019-11-05 13:05:59 +00:00
e4147f3611 altTab: Use correct actor in label height computation
Commit f2bd39b20 removed an intermediate bin, and now we use the
thumbnail bin instead of the label actor to compute the label
height, whoops.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/804
2019-11-05 12:52:29 +00:00
6a42d77261 st: Track stylesheet changes on the StThemeContext
Instead of every individual StThemeNode. There are essentially two kinds
of theme nodes: Those we create for lookups, and those interned by the
theme context and used by StWidgets. Listening to the signal on the former
is pointless as they are short lived and not meant to be really used for
drawing. So it is only essential to track stylesheet changes in those we
intern for later use.

This change does precisely that, it lets the StThemeContext track the
stylesheet changes and let all known theme nodes reset their state for
it.

The internal array holding all connected handlers for this signal in glib
was about the biggest single allocation made in gnome-shell, as interned
theme nodes nodes are around the 4 to 5 digit numbers. This essentially
makes it disappear.

This however means that widgets that are explicitly set a theme through
st_widget_set_theme() don't get their theme node implicitly updated.
There's little reasons to use that API, so perhaps this is an acceptable
tradeoff.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/779
2019-11-05 12:36:28 +00:00
55867c40c4 st: Drop StWidget theme overriding API
A StWidget could get its style from a) a theme set in the StThemeContext,
and b) directly through it's ::theme property. Generally, overriding CSS
through the latter cannot be recommended as it loses any connection with
the global theme (eg. the ones you get through selector specificity).

It sounds a bit too powerful and pervasive, there's no use for it in
gnome-shell and doesn't look like something that could be recommended on
extensions. So, just drop this piece of API.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/779
2019-11-05 12:36:28 +00:00
28c535e341 calendar-server: Uses wrong timezone for event times
The conversion to UTC/time_t time was not using correct timezone.

Closes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1714
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/806
2019-11-05 12:27:06 +01:00
f309d98bc8 cleanup: Use more template strings
xgettext got better at recognizing template strings, so we can
replace more string concatenations. Alas xgettext is still buggy
(surprise, regular expressions are hard), so there are still a
handful of holdouts that prevent us from making a complete switch.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/792
2019-11-05 01:51:29 +00:00
2c62e45168 st: Remove StBin's align properties
They are now completely unused, so remove them and stop the confusing
shadowing of ClutterActor's own x/y-align properties.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/803
2019-11-04 21:27:56 +01:00
f2bd39b20c js: Use generic actor properties to align StBin children
StBin's fill/align properties are now no-ops; get back the intended
child allocation by setting the corresponding x/y-align on the child.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/803
2019-11-04 21:27:56 +01:00
72af64d964 st: Remove st_get_align_factor() utility method
It is now unused.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/803
2019-11-04 21:23:32 +01:00
2f39bd8ba4 st/bin: Use child's align properties
By now, all containers and layout managers except StBin (and its
subclasses) use the generic ClutterActor expand/align properties
to control how their children are laid out.

This is particularly confusing as two or the properties StBin uses
for layout - x-align and y-align - shadow the generic ClutterActor
ones, but work very differently: They use a different enum and
determine how the bin lays out its child, instead of how the bin
is laid out by its parent.

Address this by deprecating the StBin properties and using the same
generic ClutterActor properties as everyone else.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/803
2019-11-04 21:23:32 +01:00
f0a5170473 st: Deprecate StBoxLayout child properties
We are no longer using them, and so shouldn't anyone else :-)

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/780
2019-11-01 19:42:02 +00:00
104071acbd js: Replace child properties
Every since commit aa394754, StBoxLayout has supported ClutterActor's
expand/align properties in addition to the container-specific child
properties. Given that that's the only container left with a special
child meta, it's time to fully embrace the generic properties (and
eventually remove the child meta).

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/780
2019-11-01 19:42:01 +00:00
4338ca5fd9 padOsd: Add missing 'closed' signal
This wasn't added on the GObject-ification in commit c4c5c4fd5c. This
introduced warnings and misbehaviors when closing the dialog. (Eg.
pressing the "show OSD" pad action would show stack different dialogs
on top each other).

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/800
2019-11-01 19:23:14 +00:00
e06421b04b layout: Drop no-clear-hint code
Mutter is doing the right thing by default, we no longer need this.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/798
2019-11-01 12:29:00 +01:00
5687035c9b appDisplay: Check instanceof AppIcon using constructor inside the class
It seems like some recent change (maybe the move to a ClutterActor
subclass for AppIcon) broke the check whether the drag source is an
instance of AppIcon. While the drag source indeed is an AppIcon and
everything else works correctly, the check still returns false, which
breaks the creation of new folders using DnD.

Theoretically it makes sense that this doesn't work, because we're
assigning AppIcon using `var AppIcon =` and that will only get set after
`GObject.register_class()` finished, so accessing `AppIcon` inside that
function seems risky and is probably wrong.

Fix this by comparing to `this.constructor` instead of `AppIcon`, which
works fine and we know for sure exists at this point.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/794
2019-10-31 19:35:37 +00:00
8cb819926a po: Sort LINGUAS
In Endless, we add support for a number of additional languages. Keeping
this file sorted makes it easier to rebase the patch which does this. No
functional change.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/797
2019-10-31 09:46:15 +00:00
284ace5b5f cleanup: Use (un)block_signal_handler() convenience wrapper
We now depend on a gjs version that is guaranteed to provide those
more idiomatic wrappers, so use them instead of the clunkier static
methods.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/795
2019-10-30 19:40:15 +00:00
7bc39ba750 ci: Run tests through dbus-run-session
Something changed recently in the test initialization code, causing
the test-theme invocation to fail. Make sure there is a D-Bus session
by running the tests through dbus-run-session to get them going again.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/796
2019-10-30 19:43:53 +01:00
aa9031d8e7 st/scroll-view: Remove scrollbars references on dispose
As we're destroying the scrollbars on destruction, we should remove any
reference of it, not to cause multiple-calls to disposal to unreference them
again.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/190
2019-10-30 01:08:27 +01:00
4dc44304df mpris: Hide notification when !CanPlay, instead of closing player
I have observed a client in the wild (Chromium again) set CanPlay to
false momentarily while it is loading the next song. Previously, the
code would close the player proxy in that case, meaning that after
playing one track, the MPRIS message would disappear and never come
back.

However, I think this use of CanPlay, while apparently not usual, is not
incorrect according to the spec. We should hide the message instead of
closing the player proxy.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1362
2019-10-29 19:25:16 +00:00
975280fc50 mpris: Validate received data against the expected types from the spec
In the wild we have buggy clients (notably Chromium 77 and earlier) that
send metadata with the wrong types. Previously, this would throw an
exception and prevent the MPRIS information from showing up in the
message list.

This changes the code to check if any incoming metadata is of the type
it is expected to be, and logs a warning if not, then continues on with
a default value.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1362
2019-10-29 19:25:16 +00:00
39e6fc9e9d js: Use Gjs GTypeName computation for all classes
As per previous commit we can remove the explicit GTypeName definitions
and use gjs auto computation for all the GObject registered classes.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/790
2019-10-29 18:38:35 +00:00
91707f4f82 environment: Use gjs smart GObject GTypeName computation
Make gjs to compute the GType name for registered GObject-derived
classes using the file basename and the first directory name, so that we
can avoid name clashing, and ensure that no extension will break the
shell by registering a name that is already used (by the shell or by any
other extension).

This requires gjs commit 02568304 [1] that will be part of release 3.35.2,
so bump the required version as gjs does post-release version bumps.

[1] https://gitlab.gnome.org/GNOME/gjs/merge_requests/337

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/790
2019-10-29 18:38:35 +00:00
b7bf9e09e9 ci: Switch to mutter's v3 docker image
As we start depending on newer stuff, switch to an image that is based
on a more recent base image to minimize the pain a bit.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/792
2019-10-29 17:38:06 +00:00
db9249a1b7 padOsd: Work around xgettext confusion
Similar to the previous work-around, xgettext gets thrown off by
embedded quotes in template strings, in particular where a template
"breaks up" a pair of quotes.

Throw in some more comments to make xgettext happy, but whoever makes
the gettext toolchain not depend on fragile regular expressions will
be drowned in beverages of their choice ...

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/792
2019-10-29 17:38:06 +00:00
10b2083d3e extensionPrefs: Trick xgettext into accepting odd number of backticks
Xgettext learned about template strings now, which is good. However
it's still buggy, so instead of the "classic" xgettext issue with
backticks, we now have exciting new issues to find work-arounds for.

One issue is that it doesn't detect backticks inside string constants
as regular characters, so having an odd number of backticks throws off
its regex.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/792
2019-10-29 17:38:06 +00:00
fa1b7a9ef5 overview: Set searchEntry offscreen-redirected always
This corrects weird-looking blending visible as it fades out when the
overview closes. Previously the entry's dark background would drown out
the text as it fades out, but now they maintain a consistent contrast ratio
during the fade.

There's no noticeable change in performance, but in theory it should be
faster as text entries don't change at full frame rate. So stage redraws
will usually have a cached searchEntry drawn and require less effort.
Though the main purpose here is to correct the appearance.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/778
2019-10-29 15:41:06 +00:00
b6e57a5ae8 closeDialog: Fix dialog size when using geometry scaling
The close dialog is added as a child to MetaWindowActor, and, in Wayland
sessions, since commit [1] MetaWindowActor applies a transformation
matrix which scales all it's children using the geometry scale factor.
Now because the dialog actor is not a window (i.e. a MetaSurfaceActor),
but a subclass of StWidget, the scale factor is also applied to the
properties of the dialog by StThemeNode, so we end up applying the
geometry scale twice to the close dialog.

Fix this by applying the inverted scale to the dialog, which leaves the
scaling only to MetaWindowActor. This means we also can't apply a pivot
point other than 0 to the dialog actor, so apply the 0.5-pivot point to
the `_dialog` child of the Dialog class (the actual visible dialog box)
and also perform scaling animations on this child.

[1] https://gitlab.gnome.org/GNOME/mutter/commit/fb9e8768

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/783
2019-10-28 17:30:50 +00:00
b6d47c18c3 windowManager: Always reset window actors when animations are cancelled
Remove all transformations from window actors after a window animation
was either cancelled or finished. Right now we only do that if the
transition finished successfully, which seems kind of pointless (it can
probably be historically explained because the callbacks inside the
"kill-window-effects" signal handler are connected to those
`*WindowDone()` functions though and the `*WindowOverwritten()`
functions were only added later in [1]).

This fixes a recent regression where a window animation would get
cancelled and remain stuck by switching workspaces. The regression
probably happened due to different behaviour of the `onOverwritten`
callback of Tweener and the `onStopped` callback of Clutter transitions:
For the workspace-switching animation the window actors get reparented
to a temporary container, which makes Clutter transititons emit
"stopped" (`clutter_actor_remove_child_internal()` stops transitions on
its children), while Tweener would continue the animation.

[1] 6dd302e5ce

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/784
2019-10-28 14:28:49 +00:00
c35b4cede5 popupMenu: Don't chain up vfuncs if the parent doesn't implement them
Some vfuncs like `button_press_event`, `button_release_event` and
`touch_event` don't have handlers in the parent classes of
PopupBaseMenuItem. So don't call those handlers and return the default
Clutter.EVENT_PROPAGATE there.

This fixes a crash of the shell that happens when pressing a mouse
button inside the system popup menu and releasing it above a slider like
the volume slider again.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/787
2019-10-28 13:43:55 +00:00
6cfcfc72cc panel: Update window section items on title changes
We currently only update the windows section when either the focus app changes,
or when the app's windows change (that is, a window is opened or closed). This
allows the menu item labels to become stale if the window title changes after
one of those events (for example when switching tabs).

Fix this by updating menu items when the corresponding window title changes.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1830
2019-10-28 12:42:28 +01:00
0732e1426a appDisplay: Don't crash if app is missing categories
g_desktop_app_info_get_categories() may return null. In that case, the
previous code would fail to create a folder when dragging an app with
no categories onto another app. Instead, simply continue with the next
app info.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/782
2019-10-25 15:48:52 -07:00
055c007ac2 dnd: Skip drag target when its acceptDrop() throws an exception
In the case of bugs in a drag target's acceptDrop() function, it may
throw an exception. In the previous code, this would break out of the
loop entirely and never cancel the drag, so the mouse button release
event would be ignored and you would have to press Esc to get out of the
drag.

In this change, if acceptDrop() throws an exception, we log it and move
on to the next parent target instead.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/777
2019-10-22 18:08:10 -07:00
43cf466d09 js: Replace Clutter.Actor.get_allocation_geometry()
The function was deprecated and has now been dropped from mutter.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/776
2019-10-21 18:41:35 +02:00
6965781d59 st: Use clutter_actor_pick() in pick
Use the new function to perform picking and avoid
going through any painting-related code paths.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/773
2019-10-21 13:53:56 +00:00
80680803aa Update Persian translation 2019-10-19 15:16:39 +00:00
51601f3ead Update shotwell desktop file name references
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/774
2019-10-18 16:50:57 +02:00
b25a73c243 authPrompt: Wiggle on failure
Add a wiggle effect to the password entry on failure. The
parameters are set as per design review during GNOME Shell
Hackfest 2019.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/768
2019-10-18 11:25:45 +00:00
d0690c3952 util: Add wiggle helper
Add Util.wiggle(), which accepts the wiggle offset, duration,
and number of times, as parameters.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/768
2019-10-18 11:25:45 +00:00
f2466caef3 environment: Parse repeat-count and auto-reverse
Those are two useful ClutterTimeline properties and
will be needed for wiggling the search entry when
failing the password.

Add support for passing repeat-count and auto-reverse
to ClutterActor.ease and ClutterActor.ease_property.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/768
2019-10-18 11:25:45 +00:00
b1d22d2058 search: Drop SearchResultInterface again
It adds a significant cost to AppIcons which are used
 - quite a log (depending on installed apps)
 - in preformance-sensitive contexts (spring animation)

Just rely on duck typing and revert 91a5133116.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1799
2019-10-17 15:56:07 +00:00
6f7e5976e2 Update Friulian translation 2019-10-17 13:54:18 +00:00
29543f369f Update Croatian translation 2019-10-17 12:08:58 +00:00
a144a1c76d workspace: Use graphene instead of clutter
This was forgotten after the graphene type port landed.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/770
2019-10-17 11:31:59 +02:00
d91927674d workspace: Sort windows in overview grid using cached center
When accessing properties on ClutterActor for size and position there is
a notable access time overhead. This overhead adds considerable user lag
when opening the overview if many windows are open.

This is primarily due to these properties being accessed while sorting
WindowClone instances by their window's center for placement in the
overview. By pre-computing this center value only once when
initializing WindowClone, the induced lag can be significantly reduced.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/763
2019-10-17 07:27:59 +00:00
d12cd12e1b ci: Make run-eslint more convenient for local use
The script can be helpful outside of CI, in particular for gradually
transitioning to the new style.

Reverting commit f00201fa6c it is already possible to do something
like

 $ CI_MERGE_REQUEST_PROJECT_URL=https://gitlab.gnome.org/GNOME/gnome-shell \
   CI_MERGE_REQUEST_TARGET_BRANCH_NAME=master CI_COMMIT_SHA=HEAD \
   .gitlab-ci/run-eslint.sh

but that is hardly convenient.

Instead, allow passing the required parameters on the command line:

 $ .gitlab-ci/run-eslint.sh origin master

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/730
2019-10-16 15:37:12 +00:00
caa50dc1a3 ci: Ensure eslint output exists
We are a long time away from an error-free eslint run,
but when we get there eventually, let's not trip over
non-existent files ...

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/730
2019-10-16 15:37:12 +00:00
55b57421dc cleanup: Replace signal connections with virtual functions
Inheriting from actors allows to use virtual functions instead of signal
connections for multiple cases, so just use them when possible.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
320df13b65 st/button: Add the clicked button to virtual function signature
clicked signal includes a clicked mouse button parameter, but the vfunc
signature doesn't include it, so it won't be passed to the functions when
the signal is emitted.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
e4920b2f80 pageIndicators: Use Clutter.Orientation as orientation parameter
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
c9fbae3408 docs: Update actor and delegate_ paragraph in HACKING
Deprecate the usage of the `actor` property, while keep the `_delegate` part
as it is needed for DnD for now.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
a3c6217875 overview: Make public properties read-only
Overview's animationInProgress, visible and visibleTarget properties are not
meant to be modified from others, but be read only.

So make this clearer using properties getters and private values.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
db7726c5bf avatar: Use Property bindings to sync reactivity
Instead of manually updating properties on change, use native properties
bindings to keep the them synchronized.

Disable hover-tracking and focus-ability when the avatar is not sensitive.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
0b91dee5a9 windowManager: Inherit WindowDimmer from Clutter.BrightnessContrastEffect
As result add the effect to the actor on the caller function.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
3838220961 calendarMessageList: Remove sections map and use clutter children
Now that the calendar message list and the message sections are actors, there's
no need to keep track of the sections in a different Map as we can just use the
native clutter functions to manage the children and access to their properties.

Also cleanup the signal connection/disconnection.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
9bb12f6f87 messageList: Use St.Bin as message container and use clutter to manage the list
When messages are added to the message list, we create a container for those,
however since now the messages are actor themselves we can just create a list
item actor that holds the message actor and refer to the message parent in order
to get their container.

This allows to remove the obj container map we used, using the native clutter
parent-child hierarchy and handle signal connections cleanly.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
4dea1f801a lookingGlass: Use resultsArea to keep track of results
Now that results are actors we can just use their container to keep
track of them

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
91a5133116 search: Define SearchResultInterface and implement valid results with it
Since all the search result classes are now GObject classes, we can enforce
the methods we want to have in there (just activate() for now) using an
interface, to make sure they are implementing what we require and to easily
group all the classes that can be used as search results, even though they
are not extending SearchResult.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
c4c5c4fd5c cleanup: Use inheritance for Actor classes instead of composition
Remove the `this.actor = ...` and `this.actor._delegate = this` patterns in most
of classes, by inheriting all the actor container classes.

Uses interfaces when needed for making sure that multiple classes will implement
some required methods or to avoid redefining the same code multiple times.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
f67b409fc1 screenshot: Pass a Graphene.Point as PickPixel 'finished' signal
This will allow to pass the data as native object when porting this to Clutter
actor.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
22fe4e92c7 screenshot: Return a Meta.Rectangle as geometry
Since the geometry is used as a signal parameter, this can't be used when using
a GObject based class, so use the Meta boxed type instead of a JS object.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
91eb84fa4e overview: Add OverviewActor and use as main actor of the Overlay
Use the Overview class as controller, while create the actual overlay actor
using a GObject-derived class.

Replace actual properties with getter-only properties.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
4e1492c926 messageTray: Dispose Source on destruction
Dispose the Source Object when dispose() is called, avoiding that it could be
called twice on a destroyed Source.

So, notify count changes before destroying the object, and don't emit this
twice on destroyNonResidentNotifications (as if a notification is destroyed
the property notify will happen in the notification destroy callback anyways).

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:13 +00:00
ed97f61750 messageTray: Dispose Notification on destroy
When the notification is destroyed we should also dispose the underneath GLib
object, and ensure that we don't dispose this twice.

In order to avoid this, don't destroy transient notifications that have been
already been removed and only destroy the resident notifications on activation
if they have not been destroyed earlier.
Thus connect after to the 'activated' signal and once the default handler has
been called destroy the notification if not requested earlier.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:12 +00:00
b5676a2a5c messageTray: Inherit Notification, Source and NotificationPolicy from GObject
Register notifications, sources and policies as GObject gtypes so that they can
be passed in signals and use native properties and signals.

Reimplement all the extending classes.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:12 +00:00
7059dcced3 keyboard: Add KeyboardManager to manage the lifetime of the keyboard actor
The Keyboard class used to be both a view and controller class, however in order
to make the keyboard a native Clutter.Actor, we need to separate the widget from
the controller class, so that we can manage the actor lifetime from the JS side.

Thus, initialize the keyboard actor on the Keyboard constructor and create a
KeyboardManager class to manage its state and lifetime.

Add proxy methods for the public functions that were used by other shell
components

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:12 +00:00
c7e0c7eb79 background: Rename Background 'changed' signal to 'bg-changed'
Meta.Background has already a 'changed' signal and not to confuse the source
signal with the wrapper one, rename the wrapper class signal into 'bg-changed'.

This will be relevant when we'll inherit from Meta.Background, as signal
emissions from the base class could interfere with the wanted derived class
behavior and with the the grouping of successive changes into a single ::change
emission.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:12 +00:00
ff775213a5 calendar: Use GDateTime for selected-date-changed signal
Since GObject derived classes can't use JS objects as signal parameters, let's
go native and use GLib.DateTime instead.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:12 +00:00
7f9c709c85 appDisplay: Use an St.Widget as base actor for FolderView
This is needed to make possible to convert BaseAppView into a St.Widget so that
all views can inherit from it.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:12 +00:00
74d7d3e259 animation: Don't sync animation sizes on change
When the actor size changes, we might incur into an allocation cycle.

This was introduced by commit b6ec02ce, but when the animation will be an actor
itself, there will be no need to update the children size on actor size update,
as this will be managed by the actor allocation cycle itself.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/1384
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:12 +00:00
0353a5bf2c cleanup: Rename signals/methods that will conflict with Clutter.Actor
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
2019-10-16 15:26:12 +00:00
ab6a629955 screenShield: Compute lock timeout fade duration using animation settings
When the screen is marked as idle, we normally start a fading animation and
a timeout to finally lock the screen. This timeout is configured using the
fade time if no longer delay is set in settings.

However if animations are disabled or slowed-down/up, the fade time is
different from the STANDARD_FADE_TIME and so we might end up showing the
lock shield without actually locking for STANDARD_FADE_TIME in the disabled
or slowed-up animations case, or locking too early in case of slowed-down
animations.

So, just adjust the timeout time using the same logic of animations so that
this value is matching all the times.

Related to https://gitlab.gnome.org/GNOME/gnome-shell/issues/1744

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/749
2019-10-16 15:20:58 +00:00
6cad251187 volume: Show indicator when microphone is active
Devices like cameras and microphones are privacy sensitive, as they can
be used to spy on the user. We cannot prevent non-sandboxed apps from
doing that, but as we already track when the microphone is recording,
we can at least show an indicator to make sure it doesn't happen behind
the user's back.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/729
2019-10-16 13:08:20 +00:00
d7c569c692 st: Remove color from ClutterActor pick virtual function
It's unused since commit mutter@14c706e51

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/759
2019-10-16 12:01:41 +00:00
0615370930 Replace Clutter.Point by Graphene.Point
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/421
2019-10-16 10:49:04 +00:00
7a92a9ba21 st: Replace ClutterSize by graphene_size_t
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/421
2019-10-16 10:49:04 +00:00
0199857c5b Replace ClutterVertex by graphene_point3d_t
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/421
2019-10-16 10:49:04 +00:00
59e3a1a816 doap: Clean up list of maintainers
Move historic entries from "maintainers" to "authors" to reflect
current reality :-(
2019-10-16 12:47:05 +02:00
6533690fff search: Activate SearchResult from the result itself
Search result views can include also objects that are not inheriting from
SearchResults (such as the AppIcon) that has not any 'activate' signal, to
connect to. Since we want to enforce a more formal interface, we want to
have just a simpler requirement as an activate() method.

So, instead using the 'activate' signal in SearchResult to activate a result
via SearchResultsBase just implement activate() in the result.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/765
2019-10-16 12:27:49 +02:00
d0d1845bb6 search: Rename SearchResults to SearchResultsView
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/765
2019-10-16 11:52:55 +02:00
20f4fc7c87 shell-screenshots: Do not pass a clip for window screenshots
Design team wants us not to clip away the shadows, lets do that.

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/762
2019-10-14 17:56:01 +02:00
b4128967a1 st/scroll-view: Remove container foreach vfunc
foreach_with_internals vfunc is deprecated for long time and not used
anymore, so remove it

Related to: https://gitlab.gnome.org/GNOME/mutter/merge_requests/816

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/747
2019-10-14 14:57:49 +00:00
38ad1d7c13 environment: Only disable unredirection of ongoing transitions
When a transition is set up with a delay, it may be removed before it
actually started. We won't get a ::stopped signal in that case, with
the result that we currently end up with a mismatched unredirection
disabling.

Address this by only disable unredirection once the transition has
actually started.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/1788
2019-10-14 10:47:36 +02:00
f78136182f Updated Spanish translation 2019-10-14 09:15:23 +02:00
252 changed files with 34508 additions and 8946 deletions

View File

@ -1,6 +0,0 @@
{
"extends": [
"./lint/eslintrc-gjs.json",
"./lint/eslintrc-shell.json"
]
}

3
.eslintrc.yml Normal file
View File

@ -0,0 +1,3 @@
extends:
- ./lint/eslintrc-gjs.yml
- ./lint/eslintrc-shell.yml

View File

@ -14,7 +14,7 @@ variables:
- merge_requests
check_commit_log:
image: registry.gitlab.gnome.org/gnome/mutter/master:v2
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stage: review
variables:
GIT_DEPTH: "100"
@ -47,7 +47,7 @@ eslint:
when: always
build:
image: registry.gitlab.gnome.org/gnome/mutter/master:v2
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stage: build
before_script:
- .gitlab-ci/checkout-mutter.sh
@ -65,14 +65,15 @@ build:
- build
test:
image: registry.gitlab.gnome.org/gnome/mutter/master:v2
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stage: test
variables:
XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/runtime-dir"
NO_AT_BRIDGE: "1"
before_script:
- ninja -C mutter/build install
script:
- xvfb-run meson test -C build --no-rebuild
- dbus-run-session -- xvfb-run meson test -C build --no-rebuild
<<: *only_default
artifacts:
expire_in: 1 day
@ -81,7 +82,7 @@ test:
when: on_failure
test-pot:
image: registry.gitlab.gnome.org/gnome/mutter/master:v2
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stage: test
before_script:
- ninja -C mutter/build install

View File

@ -1,6 +1,5 @@
#!/usr/bin/bash
shell_branch=$(git describe --contains --all HEAD)
mutter_target=
git clone https://gitlab.gnome.org/GNOME/mutter.git
@ -26,8 +25,7 @@ if [ "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then
fi
if [ -z "$mutter_target" ]; then
mutter_target=$(git branch -r -l origin/$shell_branch)
mutter_target=${mutter_target:-$(git branch -r -l ${shell_branch#remotes/})}
mutter_target=$(git branch -r -l origin/$CI_COMMIT_REF_NAME)
mutter_target=${mutter_target:-origin/master}
echo Using $mutter_target instead
fi

View File

@ -13,11 +13,17 @@ is_empty() {
}
run_eslint() {
ARGS_LEGACY='--config lint/eslintrc-legacy.json'
ARGS_LEGACY='--config lint/eslintrc-legacy.yml'
local extra_args=ARGS_$1
local output=OUTPUT_$1
eslint -f unix ${!extra_args} -o ${!output} js
local output_var=OUTPUT_$1
local output=${!output_var}
# ensure output exists even if eslint doesn't report any errors
mkdir -p $(dirname $output)
touch $output
eslint -f unix ${!extra_args} -o $output js
}
list_commit_range_additions() {
@ -70,10 +76,13 @@ create_common() {
# non-legacy style just yet ...
unset CI_MERGE_REQUEST_TARGET_BRANCH_NAME
if [ "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then
git fetch $CI_MERGE_REQUEST_PROJECT_URL.git $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
REMOTE=${1:-$CI_MERGE_REQUEST_PROJECT_URL.git}
BRANCH_NAME=${2:-$CI_MERGE_REQUEST_TARGET_BRANCH_NAME}
if [ "$BRANCH_NAME" ]; then
git fetch $REMOTE $BRANCH_NAME
branch_point=$(git merge-base HEAD FETCH_HEAD)
commit_range=$branch_point...$CI_COMMIT_SHA
commit_range=$branch_point...HEAD
list_commit_range_additions $commit_range > $LINE_CHANGES
@ -96,7 +105,7 @@ if ! is_empty $OUTPUT_FINAL; then
fi
# Just show the report and succeed when not testing a MR
if [ -z "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then
if [ -z "$BRANCH_NAME" ]; then
exit 0
fi

View File

@ -163,11 +163,17 @@ you to inherit from a type to use it, you can do so:
return [100, 100];
}
vfunc_paint() {
vfunc_paint(paintContext) {
let framebuffer = paintContext.get_framebuffer();
let coglContext = framebuffer.get_context();
let alloc = this.get_allocation_box();
Cogl.set_source_color4ub(255, 0, 0, 255);
Cogl.rectangle(alloc.x1, alloc.y1,
alloc.x2, alloc.y2);
let pipeline = new Cogl.Pipeline(coglContext);
pipeline.set_color4ub(255, 0, 0, 255);
framebuffer.draw_rectangle(pipeline,
alloc.x1, alloc.y1,
alloc.x2, alloc.y2);
}
});
```
@ -186,15 +192,27 @@ and "double quotes" for strings that the user may see. This allows us to
quickly find untranslated or mistranslated strings by grepping through the
sources for double quotes without a gettext call around them.
## `actor` and `_delegate`
## `actor` (deprecated) and `_delegate`
gjs allows us to set so-called "expando properties" on introspected objects,
allowing us to treat them like any other. Because the Shell was built before
you could inherit from GTypes natively in JS, we usually have a wrapper class
that has a property called `actor`. We call this wrapper class the "delegate".
you could inherit from GTypes natively in JS, in some cases we have a wrapper
class that has a property called `actor` (now deprecated). We call this
wrapper class the "delegate".
We sometimes use expando properties to set a property called `_delegate` on
the actor itself:
```javascript
var MyActor = GObject.registerClass(
class MyActor extends Clutter.Actor {
_init(params) {
super._init(params);
this._delegate = this;
}
});
```
Or using the deprecated `actor`:
```javascript
var MyClass = class {
constructor() {
@ -215,6 +233,7 @@ delegate object from an associated actor. For instance, the drag and drop
system calls the `handleDragOver` function on the delegate of a "drop target"
when the user drags an item over it. If you do not set the `_delegate`
property, your actor will not be able to be dropped onto.
In case the class is an actor itself, the `_delegate` can be just set to `this`.
## Functional style

65
NEWS
View File

@ -1,3 +1,68 @@
3.35.2
======
* Fix unredirection after cancelled animations [Florian; #1788]
* Include shadow in window screenshots [Robert; !762]
* Show indicator when microphone is active [Florian; !729]
* Use inheritance instead of delegate pattern [Marco; !559]
* Use cached coordinates for window sorting in overview [Andrew; !763]
* Wiggle login/unlock password entries on failure [Georges; !769]
* Update window titles in app menu [Florian; #1830]
* Fix window animations getting stuck by workspace switches [Jonas D.; !784]
* Fix not-responding dialog size when using geometry scaling [Jonas D.; !783]
* Handle buggy MPRIS clients more gracefully [Philip; #1362]
* Deprecate StBoxLayout's child properties [Florian; !780]
* Remove StBin's align properties [Florian; !803]
* Use correct timezones for events [Milan, Florian; !806, #1895]
* Reduce overhead of tracking stylesheet changes [Carlos; !779]
* Replace action icons in system menu with regular menu items [Florian; #270]
* Refine polkit dialogs [Jonas D.; !788]
* Fix battery icon glitch in "100% but charging" case [Philip; !814]
* Fix windows getting stuck on screen if closed while animating [Florian; !815]
* Use font from interface settings [Florian; #688288]
* Show polkit confirmation dialog for users with no password
[Joaquim, Jonas D.; !829]
* Use better OSK layout fallback for unsupported variants [Florian; #1907]
* Hide stopped spinner in top bar [Joonas; !832]
* Reuse existing icons when updating the app picker grid [Georges; !841]
* Show switcher popups immediately on second key press [Florian; #1928]
* Add position-based animation to page indicators [Alexander; !843]
* Improve modifier-less keyboard navigation of switcher popups [Florian; #1883]
* Improve weather integration [Florian; #1927, #1926]
* Add back sound feedback when scrolling volume indicator [Florian; #53]
* Fix creating app folders with no pre-existing folders [Jonas D.; #1652]
* Improve DND page switching in app picker [Florian, Jonas D.; #1693]
* Fix disable command of gnome-extensions tool [Florian; #1946]
* Tweak styling of notifications/media constrols [Joonas; !855, !865]
* Enable clean session shutdown after gnome-shell failure [Benjamin; !858]
* Also remove scaled keys when texture cache is cleared [Daniel M.; !567]
* Don't show overflow indicator in switchers that fit screen [Florian; #1834]
* Move libcroco dependency in-tree [Federico; !861]
* Move to app folder location when it is created/renamed [Georges; !883]
* Dismiss switcher popups when a system modal dialogs opens [Florian; #1536]
* Fix weather forecasts for automatic location when Weather is not sandboxed
[Florian; #1823]
* Place launched applications into a systemd scope [Benjamin; !863]
* Fixed crashes [Jonas D., Carlos; !787, !813]
* Misc. bug fixes and cleanups [Marco, Georges, Daniel V., Florian, Robert,
Kalev, Philip, Jonas D., Will, Carlos, Jonas Å., cunidev, Joonas, Federico;
!747, !765, !421, !759, !749, !730, !770, #1799, !774, !773, !776, !777,
!782, !794, !778, !792, !790, !190, !796, !795, !797, !798, !800, !804, !808,
!807, !810, !811, !563, !809, !805, !817, !818, !822, !830, !828, !823, !835,
!840, !842, !833, !845, !846, !847, !851, #1916, !862, !866, #1979, !827,
#1976, !884, !873, !885, !799, !887, !891, !816]
Contributors:
Marco Trevisan (Treviño), Benjamin Berg, Philip Chimento, Milan Crha,
Jonas Dreßler, Carlos Garnacho, Joonas Henriksson, Kalev Lember, Robert Mader,
Alexander Mikhaylenko, Daniel García Moreno, Florian Müllner,
Georges Basile Stavracas Neto, Federico Mena Quintero, Joaquim Rocha,
Will Thompson, Daniel van Vugt, Andrew Watson, cunidev, Jonas Ådahl
Translators:
Daniel Mustieles [es], Goran Vidović [hr], Fabio Tomat [fur],
Danial Behzadi [fa], Andika Triwidada [id], Efstathios Iosifidis [el],
Ricardo Silva Veloso [pt_BR]
3.35.1
======
* Misc. bug fixes and cleanups [Marco; Matthias; !758, #701212]

View File

@ -1,11 +1,12 @@
[Unit]
Description=Disable GNOME Shell extensions after failure
# Note that this unit must not conflict with anything, and must
# be able to run in parallel with the gnome-session-shutdown.target.
DefaultDependencies=no
# Only disable extensions for a short period of time after login.
# This means we err on the side of failing the first login after a broken
# extension was installed.
Requisite=gnome-session-stable.timer
# We want to disable extensions only if gnome-shell has flagged the extensions
# to be a likely cause of trouble.
ConditionPathExists=%t/gnome-shell-disable-extensions
[Service]
Type=simple

View File

@ -50,7 +50,7 @@
</description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
<default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'org.gnome.Shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
<summary>List of desktop file IDs for favorite applications</summary>
<description>
The applications corresponding to these identifiers

View File

@ -36,10 +36,8 @@ $_hover_bg_color: lighten($bg_color,if($variant=='light', 5%, 3%));
$_active_bg_color: if($variant == 'light', darken($bg_color, 14%), darken($bg_color, 9%));
$font-size: 11;
$font-family: Cantarell, Sans-Serif;
stage {
font-family: $font-family;
@include fontsize($font-size);
color: $fg_color;
}
@ -979,6 +977,7 @@ StScrollBar {
spacing-columns: 0.8em;
}
.weather-header-box,
.weather-box {
spacing: 0.4em;
}
@ -1061,9 +1060,9 @@ StScrollBar {
}
.calendar-today {
font-weight: bold;
//color: lighten($fg_color,10%);
//background-color: darken($bg_color,5%);
border: 1px solid $_bubble_borders_color;
color: lighten($fg_color,5%);
background-color: darken($bg_color,5%);
// border: 1px solid lighten($_bubble_borders_color,20%);
}
.calendar-day-with-events {
color: lighten($fg_color,10%);
@ -1153,14 +1152,21 @@ StScrollBar {
padding: 10px;
}
.message-close-button {
color: lighten($fg_color, 15%);
&:hover { color: if($variant=='light', lighten($fg_color, 30%), darken($fg_color, 10%)); }
&:active { color: if($variant=='light', lighten($fg_color, 40%), darken($fg_color, 20%)); }
}
.message-media-control {
padding: 12px;
color: lighten($fg_color, 15%);
&:last-child:ltr { padding-right: 18px; }
&:last-child:rtl { padding-left: 18px; }
&:hover { color: $fg_color; }
&:insensitive { color: darken($fg_color,40%); }
&:hover { color: if($variant=='light', lighten($fg_color, 30%), darken($fg_color, 10%)); }
&:active { color: if($variant=='light', lighten($fg_color, 40%), darken($fg_color, 20%)); }
&:insensitive { color: if($variant=='light', lighten($fg_color, 50%), darken($fg_color, 40%)); }
}
.media-message-cover-icon {
@ -1190,7 +1196,8 @@ StScrollBar {
.aggregate-menu {
min-width: 21em;
.popup-menu-icon { padding: 0 4px; }
.popup-menu-icon { padding: 0 4px;
-st-icon-style: symbolic; }
.popup-sub-menu .popup-menu-item > :first-child {
&:ltr { /* 12px spacing + 2*4px padding */
padding-left: 20px; margin-left: 1.09em; }
@ -1199,27 +1206,6 @@ StScrollBar {
}
}
.system-menu-action {
-st-icon-style: symbolic;
color: $fg_color;
border-radius: 32px; /* wish we could do 50% */
padding: 13px;
border: 1px solid $_bubble_borders_color;
&:hover, &:focus {
background-color: $_hover_bg_color;
color: $fg_color;
border: none;
padding: 14px;
}
&:active {
background-color: $selected_bg_color;
color: $selected_fg_color;
}
& > StIcon { icon-size: 16px; }
}
// Activities Ripples
.ripple-box {
width: 52px;
@ -1571,20 +1557,14 @@ StScrollBar {
}
.page-indicator {
padding: 15px 20px;
padding: 7px 16px;
.page-indicator-icon {
width: 12px;
height: 12px;
background-color: transparent;
border: 2px solid rgba(255, 255, 255, 0.4);
border-radius: 12px;
background-color: white;
border-radius: 6px;
}
&:hover .page-indicator-icon { border-color: white; }
&:active .page-indicator-icon { border: none; margin: 2px; background-color: white; }
&:checked .page-indicator-icon,
&:checked:active .page-indicator-icon { background-color: white;}
}
.no-frequent-applications-label { @extend %status_text; }
@ -1784,8 +1764,8 @@ StScrollBar {
padding: 4px 4px;
.page-indicator-icon {
width: 6px;
height: 6px
width: 8px;
height: 8px;
}
}
}

View File

@ -31,34 +31,34 @@ its dependencies to build from tarballs.</description>
<programming-language>JavaScript</programming-language>
<programming-language>C</programming-language>
<maintainer>
<author>
<foaf:Person>
<foaf:name>William Jon McCann</foaf:name>
<foaf:mbox rdf:resource="mailto:jmccann@redhat.com" />
<gnome:userid>mccann</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
</author>
<author>
<foaf:Person>
<foaf:name>Owen Taylor</foaf:name>
<foaf:mbox rdf:resource="mailto:otaylor@redhat.com" />
<gnome:userid>otaylor</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
</author>
<author>
<foaf:Person>
<foaf:name>Colin Walters</foaf:name>
<foaf:mbox rdf:resource="mailto:walters@verbum.org" />
<gnome:userid>walters</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
</author>
<author>
<foaf:Person>
<foaf:name>Marina Zhurakhinskaya</foaf:name>
<foaf:mbox rdf:resource="mailto:marinaz@redhat.com" />
<gnome:userid>marinaz</gnome:userid>
</foaf:Person>
</maintainer>
</author>
<maintainer>
<foaf:Person>
<foaf:name>Florian Müllner</foaf:name>

View File

@ -23,14 +23,13 @@ function stripPrefix(string, prefix) {
return string;
}
var Application = GObject.registerClass({
GTypeName: 'ExtensionPrefs_Application'
}, class Application extends Gtk.Application {
var Application = GObject.registerClass(
class Application extends Gtk.Application {
_init() {
GLib.set_prgname('gnome-shell-extension-prefs');
super._init({
application_id: 'org.gnome.shell.ExtensionPrefs',
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
});
this._startupUuid = null;
@ -61,12 +60,12 @@ var Application = GObject.registerClass({
let dialog = new Gtk.Window({
modal: !this._skipMainWindow,
type_hint: Gdk.WindowTypeHint.DIALOG
type_hint: Gdk.WindowTypeHint.DIALOG,
});
dialog.set_titlebar(new Gtk.HeaderBar({
show_close_button: true,
title: row.name,
visible: true
visible: true,
}));
if (this._skipMainWindow) {
@ -89,20 +88,20 @@ var Application = GObject.registerClass({
_buildErrorUI(row, exc) {
let scroll = new Gtk.ScrolledWindow({
hscrollbar_policy: Gtk.PolicyType.NEVER,
propagate_natural_height: true
propagate_natural_height: true,
});
let box = new Gtk.Box({
orientation: Gtk.Orientation.VERTICAL,
spacing: 12,
margin: 100,
margin_bottom: 60
margin_bottom: 60,
});
scroll.add(box);
let label = new Gtk.Label({
label: '<span size="x-large">%s</span>'.format(_("Somethings gone wrong")),
use_markup: true
use_markup: true,
});
label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
box.add(label);
@ -110,13 +109,13 @@ var Application = GObject.registerClass({
label = new Gtk.Label({
label: _("Were very sorry, but theres been a problem: the settings for this extension cant be displayed. We recommend that you report the issue to the extension authors."),
justify: Gtk.Justification.CENTER,
wrap: true
wrap: true,
});
box.add(label);
let expander = new Expander({
label: _("Technical Details"),
margin_top: 12
margin_top: 12,
});
box.add(expander);
@ -127,14 +126,14 @@ var Application = GObject.registerClass({
let buffer = new Gtk.TextBuffer({ text: errortext });
let textview = new Gtk.TextView({
buffer: buffer,
buffer,
wrap_mode: Gtk.WrapMode.WORD,
monospace: true,
editable: false,
top_margin: 12,
bottom_margin: 12,
left_margin: 12,
right_margin: 12
right_margin: 12,
});
let toolbar = new Gtk.Toolbar();
@ -150,7 +149,7 @@ var Application = GObject.registerClass({
let copyButton = new Gtk.ToolButton({
icon_name: 'edit-copy-symbolic',
tooltip_text: _("Copy Error")
tooltip_text: _("Copy Error"),
});
toolbar.add(copyButton);
@ -159,15 +158,15 @@ var Application = GObject.registerClass({
// markdown for pasting in gitlab issues
let lines = [
`The settings of extension ${row.uuid} had an error:`,
'```',
'```', // '`' (xgettext throws up on odd number of backticks)
`${exc}`,
'```',
'```', // '`'
'',
'Stack trace:',
'```',
'```', // '`'
exc.stack.replace(/\n$/, ''), // stack without trailing newline
'```',
''
'```', // '`'
'',
];
clipboard.set_text(lines.join('\n'), -1);
});
@ -180,7 +179,7 @@ var Application = GObject.registerClass({
label: _("Homepage"),
tooltip_text: _("Visit extension homepage"),
no_show_all: true,
visible: row.url != null
visible: row.url != null,
});
toolbar.add(urlButton);
@ -190,7 +189,7 @@ var Application = GObject.registerClass({
});
let expandedBox = new Gtk.Box({
orientation: Gtk.Orientation.VERTICAL
orientation: Gtk.Orientation.VERTICAL,
});
expandedBox.add(textview);
expandedBox.add(toolbar);
@ -220,7 +219,7 @@ var Application = GObject.registerClass({
Gio.SettingsBindFlags.INVERT_BOOLEAN);
this._mainStack = new Gtk.Stack({
transition_type: Gtk.StackTransitionType.CROSSFADE
transition_type: Gtk.StackTransitionType.CROSSFADE,
});
this._window.add(this._mainStack);
@ -259,23 +258,15 @@ var Application = GObject.registerClass({
}
_onExtensionStateChanged(proxy, senderName, [uuid, newState]) {
let extension = ExtensionUtils.deserializeExtension(newState);
let row = this._findExtensionRow(uuid);
if (row) {
let { state } = ExtensionUtils.deserializeExtension(newState);
if (state == ExtensionState.UNINSTALLED)
if (extension.state === ExtensionState.UNINSTALLED)
row.destroy();
return; // we only deal with new and deleted extensions here
}
this._shellProxy.GetExtensionInfoRemote(uuid, ([serialized]) => {
let extension = ExtensionUtils.deserializeExtension(serialized);
if (!extension)
return;
// check the extension wasn't added in between
if (this._findExtensionRow(uuid) != null)
return;
this._addExtensionRow(extension);
});
this._addExtensionRow(extension);
}
_scanExtensions() {
@ -361,20 +352,20 @@ var Expander = GObject.registerClass({
'label', 'label', 'label',
GObject.ParamFlags.READWRITE,
null
)
}
),
},
}, class Expander extends Gtk.Box {
_init(params = {}) {
this._labelText = null;
super._init(Object.assign(params, {
orientation: Gtk.Orientation.VERTICAL,
spacing: 0
spacing: 0,
}));
this._frame = new Gtk.Frame({
shadow_type: Gtk.ShadowType.IN,
hexpand: true
hexpand: true,
});
let eventBox = new Gtk.EventBox();
@ -382,12 +373,12 @@ var Expander = GObject.registerClass({
let hbox = new Gtk.Box({
spacing: 6,
margin: 12
margin: 12,
});
eventBox.add(hbox);
this._arrow = new Gtk.Image({
icon_name: 'pan-end-symbolic'
icon_name: 'pan-end-symbolic',
});
hbox.add(this._arrow);
@ -397,7 +388,7 @@ var Expander = GObject.registerClass({
this._revealer = new Gtk.Revealer();
this._childBin = new Gtk.Frame({
shadow_type: Gtk.ShadowType.IN
shadow_type: Gtk.ShadowType.IN,
});
this._revealer.add(this._childBin);
@ -415,7 +406,7 @@ var Expander = GObject.registerClass({
this._gesture = new Gtk.GestureMultiPress({
widget: this._frame,
button: 0,
exclusive: true
exclusive: true,
});
this._gesture.connect('released', (gesture, nPress) => {
if (nPress == 1)
@ -460,7 +451,7 @@ class EmptyPlaceholder extends Gtk.Box {
super._init({
orientation: Gtk.Orientation.VERTICAL,
spacing: 6,
margin: 32
margin: 32,
});
let image = new Gtk.Image({
@ -468,15 +459,15 @@ class EmptyPlaceholder extends Gtk.Box {
pixel_size: 96,
visible: true,
vexpand: true,
valign: Gtk.Align.END
valign: Gtk.Align.END,
});
image.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
this.add(image);
let label = new Gtk.Label({
label: `<b><span size="x-large">${_("No Extensions Installed" )}</span></b>`,
label: `<b><span size="x-large">${_("No Extensions Installed")}</span></b>`,
use_markup: true,
visible: true
visible: true,
});
label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
this.add(label);
@ -491,9 +482,9 @@ class EmptyPlaceholder extends Gtk.Box {
visible: true,
max_width_chars: 50,
hexpand: true,
vexpand: (appInfo == null),
vexpand: appInfo == null,
halign: Gtk.Align.CENTER,
valign: Gtk.Align.START
valign: Gtk.Align.START,
});
this.add(desc);
@ -501,14 +492,14 @@ class EmptyPlaceholder extends Gtk.Box {
let button = new Gtk.Button({
label: _("Browse in Software"),
image: new Gtk.Image({
icon_name: "org.gnome.Software-symbolic"
icon_name: "org.gnome.Software-symbolic",
}),
always_show_image: true,
margin_top: 12,
visible: true,
halign: Gtk.Align.CENTER,
valign: Gtk.Align.START,
vexpand: true
vexpand: true,
});
this.add(button);
@ -527,13 +518,13 @@ class NoShellPlaceholder extends Gtk.Box {
orientation: Gtk.Orientation.VERTICAL,
spacing: 12,
margin: 100,
margin_bottom: 60
margin_bottom: 60,
});
let label = new Gtk.Label({
label: '<span size="x-large">%s</span>'.format(
_("Somethings gone wrong")),
use_markup: true
use_markup: true,
});
label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
this.add(label);
@ -541,7 +532,7 @@ class NoShellPlaceholder extends Gtk.Box {
label = new Gtk.Label({
label: _("Were very sorry, but it was not possible to get the list of installed extensions. Make sure you are logged into GNOME and try again."),
justify: Gtk.Justification.CENTER,
wrap: true
wrap: true,
});
this.add(label);
@ -578,11 +569,11 @@ class ExtensionRow extends Gtk.ListBoxRow {
return;
this._extension = ExtensionUtils.deserializeExtension(newState);
let state = (this._extension.state == ExtensionState.ENABLED);
let state = this._extension.state == ExtensionState.ENABLED;
GObject.signal_handler_block(this._switch, this._notifyActiveId);
this._switch.block_signal_handler(this._notifyActiveId);
this._switch.state = state;
GObject.signal_handler_unblock(this._switch, this._notifyActiveId);
this._switch.unblock_signal_handler(this._notifyActiveId);
this._switch.sensitive = this._canToggle();
});
@ -649,7 +640,7 @@ class ExtensionRow extends Gtk.ListBoxRow {
this._switch = new Gtk.Switch({
valign: Gtk.Align.CENTER,
sensitive: this._canToggle(),
state: this._extension.state === ExtensionState.ENABLED
state: this._extension.state === ExtensionState.ENABLED,
});
this._notifyActiveId = this._switch.connect('notify::active', () => {
if (this._switch.active)
@ -666,12 +657,12 @@ class ExtensionRow extends Gtk.ListBoxRow {
}
get prefsModule() {
// give extension prefs access to their own extension object
ExtensionUtils.getCurrentExtension = () => this._extension;
if (!this._prefsModule) {
ExtensionUtils.installImporter(this._extension);
// give extension prefs access to their own extension object
ExtensionUtils.getCurrentExtension = () => this._extension;
this._prefsModule = this._extension.imports.prefs;
this._prefsModule.init(this._extension.metadata);
}
@ -692,7 +683,7 @@ function initEnvironment() {
log(`ERROR: ${s}`);
},
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']),
};
String.prototype.format = Format.format;

View File

@ -1,11 +1,12 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported AuthPrompt */
const { Clutter, Pango, Shell, St } = imports.gi;
const Signals = imports.signals;
const { Clutter, GObject, Pango, Shell, St } = imports.gi;
const Animation = imports.ui.animation;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
const Util = imports.misc.util;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const UserWidget = imports.ui.userWidget;
@ -16,25 +17,42 @@ var DEFAULT_BUTTON_WELL_ANIMATION_TIME = 300;
var MESSAGE_FADE_OUT_ANIMATION_TIME = 500;
const WIGGLE_OFFSET = 6;
const WIGGLE_DURATION = 65;
const N_WIGGLES = 3;
var AuthPromptMode = {
UNLOCK_ONLY: 0,
UNLOCK_OR_LOG_IN: 1
UNLOCK_OR_LOG_IN: 1,
};
var AuthPromptStatus = {
NOT_VERIFYING: 0,
VERIFYING: 1,
VERIFICATION_FAILED: 2,
VERIFICATION_SUCCEEDED: 3
VERIFICATION_SUCCEEDED: 3,
};
var BeginRequestType = {
PROVIDE_USERNAME: 0,
DONT_PROVIDE_USERNAME: 1
DONT_PROVIDE_USERNAME: 1,
};
var AuthPrompt = class {
constructor(gdmClient, mode) {
var AuthPrompt = GObject.registerClass({
Signals: {
'cancelled': {},
'failed': {},
'next': {},
'prompted': {},
'reset': { param_types: [GObject.TYPE_UINT] },
},
}, class AuthPrompt extends St.BoxLayout {
_init(gdmClient, mode) {
super._init({
style_class: 'login-dialog-prompt-layout',
vertical: true,
});
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this._gdmClient = gdmClient;
@ -46,7 +64,7 @@ var AuthPrompt = class {
else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
reauthenticationOnly = false;
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly });
this._userVerifier.connect('ask-question', this._onAskQuestion.bind(this));
this._userVerifier.connect('show-message', this._onShowMessage.bind(this));
@ -60,70 +78,68 @@ var AuthPrompt = class {
this.connect('next', () => {
this.updateSensitivity(false);
this.startSpinning();
if (this._queryingService) {
if (this._queryingService)
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
} else {
else
this._preemptiveAnswer = this._entry.text;
}
});
this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this.actor.connect('destroy', this._onDestroy.bind(this));
this.actor.connect('key-press-event', (actor, event) => {
if (event.get_key_symbol() == Clutter.KEY_Escape)
this.cancel();
return Clutter.EVENT_PROPAGATE;
this.connect('destroy', this._onDestroy.bind(this));
this._userWell = new St.Bin({
x_expand: true,
y_expand: true,
});
this.add_child(this._userWell);
this._label = new St.Label({
style_class: 'login-dialog-prompt-label',
x_expand: false,
y_expand: true,
});
this._userWell = new St.Bin({ x_fill: true,
x_align: St.Align.START });
this.actor.add(this._userWell,
{ x_align: St.Align.START,
x_fill: true,
y_fill: true,
expand: true });
this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
this.actor.add(this._label,
{ expand: true,
x_fill: false,
y_fill: true,
x_align: St.Align.START });
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this.add_child(this._label);
this._entry = new St.Entry({
style_class: 'login-dialog-prompt-entry',
can_focus: true,
x_expand: false,
y_expand: true,
});
ShellEntry.addContextMenu(this._entry, { isPassword: true, actionMode: Shell.ActionMode.NONE });
this.actor.add(this._entry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this.add_child(this._entry);
this._entry.grab_key_focus();
this._message = new St.Label({ opacity: 0,
styleClass: 'login-dialog-message' });
this._message = new St.Label({
opacity: 0,
styleClass: 'login-dialog-message',
x_expand: false,
y_expand: true,
y_align: Clutter.ActorAlign.START,
});
this._message.clutter_text.line_wrap = true;
this._message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
this.add_child(this._message);
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
vertical: false });
this.actor.add(this._buttonBox,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this._buttonBox = new St.BoxLayout({
style_class: 'login-dialog-button-box',
vertical: false,
y_align: Clutter.ActorAlign.END,
});
this.add_child(this._buttonBox);
this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._defaultButtonWellActor = null;
this._defaultButtonWell = new St.Widget({
layout_manager: new Clutter.BinLayout(),
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER,
});
this._initButtons();
this._spinner = new Animation.Spinner(DEFAULT_BUTTON_WELL_ICON_SIZE);
this._spinner.actor.opacity = 0;
this._spinner.actor.show();
this._defaultButtonWell.add_child(this._spinner.actor);
this._spinner.opacity = 0;
this._spinner.show();
this._defaultButtonWell.add_child(this._spinner);
}
_onDestroy() {
@ -131,39 +147,39 @@ var AuthPrompt = class {
this._userVerifier = null;
}
_initButtons() {
this.cancelButton = new St.Button({ style_class: 'modal-dialog-button button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Cancel") });
this.cancelButton.connect('clicked', () => this.cancel());
this._buttonBox.add(this.cancelButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
vfunc_key_press_event(keyPressEvent) {
if (keyPressEvent.keyval == Clutter.KEY_Escape)
this.cancel();
return Clutter.EVENT_PROPAGATE;
}
this._buttonBox.add(this._defaultButtonWell,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this.nextButton = new St.Button({ style_class: 'modal-dialog-button button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Next") });
_initButtons() {
this.cancelButton = new St.Button({
style_class: 'modal-dialog-button button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Cancel"),
x_expand: true,
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.END,
});
this.cancelButton.connect('clicked', () => this.cancel());
this._buttonBox.add_child(this.cancelButton);
this._buttonBox.add_child(this._defaultButtonWell);
this.nextButton = new St.Button({
style_class: 'modal-dialog-button button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Next"),
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
});
this.nextButton.connect('clicked', () => this.emit('next'));
this.nextButton.add_style_pseudo_class('default');
this._buttonBox.add(this.nextButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.END });
this._buttonBox.add_child(this.nextButton);
this._updateNextButtonSensitivity(this._entry.text.length > 0);
@ -241,6 +257,12 @@ var AuthPrompt = class {
this.updateSensitivity(canRetry);
this.setActorInDefaultButtonWell(null);
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
Util.wiggle(this._entry, {
offset: WIGGLE_OFFSET,
duration: WIGGLE_DURATION,
wiggleCount: N_WIGGLES,
});
}
_onVerificationComplete() {
@ -269,13 +291,13 @@ var AuthPrompt = class {
oldActor.remove_all_transitions();
let wasSpinner;
if (oldActor == this._spinner.actor)
if (oldActor == this._spinner)
wasSpinner = true;
else
wasSpinner = false;
let isSpinner;
if (actor == this._spinner.actor)
if (actor == this._spinner)
isSpinner = true;
else
isSpinner = false;
@ -299,7 +321,7 @@ var AuthPrompt = class {
if (this._spinner)
this._spinner.stop();
}
}
},
});
}
}
@ -308,22 +330,23 @@ var AuthPrompt = class {
if (isSpinner)
this._spinner.play();
if (!animate)
if (!animate) {
actor.opacity = 255;
else
} else {
actor.ease({
opacity: 255,
duration: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
mode: Clutter.AnimationMode.LINEAR
mode: Clutter.AnimationMode.LINEAR,
});
}
}
this._defaultButtonWellActor = actor;
}
startSpinning() {
this.setActorInDefaultButtonWell(this._spinner.actor, true);
this.setActorInDefaultButtonWell(this._spinner, true);
}
stopSpinning() {
@ -369,7 +392,7 @@ var AuthPrompt = class {
this._message.ease({
opacity: 0,
duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
@ -404,9 +427,9 @@ var AuthPrompt = class {
this._entry.clutter_text.editable = sensitive;
}
hide() {
vfunc_hide() {
this.setActorInDefaultButtonWell(null, true);
this.actor.hide();
super.vfunc_hide();
this._message.opacity = 0;
this.setUser(null);
@ -422,7 +445,8 @@ var AuthPrompt = class {
if (user) {
let userWidget = new UserWidget.UserWidget(user);
this._userWell.set_child(userWidget.actor);
userWidget.x_align = Clutter.ActorAlign.START;
this._userWell.set_child(userWidget);
}
}
@ -501,11 +525,10 @@ var AuthPrompt = class {
}
cancel() {
if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) {
if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED)
return;
}
this.reset();
this.emit('cancelled');
}
};
Signals.addSignalMethods(AuthPrompt.prototype);
});

View File

@ -112,13 +112,12 @@ var Batch = class extends Task {
for (let i = 0; i < tasks.length; i++) {
let task;
if (tasks[i] instanceof Task) {
if (tasks[i] instanceof Task)
task = tasks[i];
} else if (typeof tasks[i] == 'function') {
else if (typeof tasks[i] == 'function')
task = new Task(scope, tasks[i]);
} else {
else
throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
}
this.tasks.push(task);
}
@ -129,9 +128,8 @@ var Batch = class extends Task {
}
runTask() {
if (!(this._currentTaskIndex in this.tasks)) {
if (!(this._currentTaskIndex in this.tasks))
return null;
}
return this.tasks[this._currentTaskIndex].run();
}
@ -179,9 +177,8 @@ var ConcurrentBatch = class extends Batch {
process() {
let hold = this.runTask();
if (hold) {
if (hold)
this.hold.acquireUntilAfter(hold);
}
// Regardless of the state of the just run task,
// fire off the next one, so all the tasks can run

View File

@ -20,7 +20,7 @@ function FprintManager() {
g_interface_info: FprintManagerInfo,
g_name: 'net.reactivated.Fprint',
g_object_path: '/net/reactivated/Fprint/Manager',
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
try {
self.init(null);

View File

@ -19,7 +19,6 @@
const { AccountsService, Atk, Clutter, Gdm, Gio,
GLib, GObject, Meta, Pango, Shell, St } = imports.gi;
const Signals = imports.signals;
const AuthPrompt = imports.gdm.authPrompt;
const Batch = imports.gdm.batch;
@ -39,72 +38,81 @@ const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 48;
const _MAX_BOTTOM_MENU_ITEMS = 5;
var UserListItem = class {
constructor(user) {
var UserListItem = GObject.registerClass({
Signals: { 'activate': {} },
}, class UserListItem extends St.Button {
_init(user) {
let layout = new St.BoxLayout({
vertical: true,
x_align: Clutter.ActorAlign.START,
});
super._init({
style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
x_expand: true,
child: layout,
reactive: true,
});
this.user = user;
this._userChangedId = this.user.connect('changed',
this._onUserChanged.bind(this));
let layout = new St.BoxLayout({ vertical: true });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: layout,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this.actor.connect('destroy', this._onDestroy.bind(this));
this.actor.connect('key-focus-in', () => {
this._setSelected(true);
});
this.actor.connect('key-focus-out', () => {
this._setSelected(false);
});
this.actor.connect('notify::hover', () => {
this._setSelected(this.actor.hover);
this.connect('destroy', this._onDestroy.bind(this));
this.connect('notify::hover', () => {
this._setSelected(this.hover);
});
this._userWidget = new UserWidget.UserWidget(this.user);
layout.add(this._userWidget.actor);
layout.add(this._userWidget);
this._userWidget.actor.bind_property('label-actor', this.actor, 'label-actor',
GObject.BindingFlags.SYNC_CREATE);
this._userWidget.bind_property('label-actor', this, 'label-actor',
GObject.BindingFlags.SYNC_CREATE);
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0,
visible: false });
layout.add(this._timedLoginIndicator);
this.actor.connect('clicked', this._onClicked.bind(this));
this._onUserChanged();
}
vfunc_key_focus_in() {
super.vfunc_key_focus_in();
this._setSelected(true);
}
vfunc_key_focus_out() {
super.vfunc_key_focus_out();
this._setSelected(false);
}
_onUserChanged() {
this._updateLoggedIn();
}
_updateLoggedIn() {
if (this.user.is_logged_in())
this.actor.add_style_pseudo_class('logged-in');
this.add_style_pseudo_class('logged-in');
else
this.actor.remove_style_pseudo_class('logged-in');
this.remove_style_pseudo_class('logged-in');
}
_onDestroy() {
this.user.disconnect(this._userChangedId);
}
_onClicked() {
vfunc_clicked() {
this.emit('activate');
}
_setSelected(selected) {
if (selected) {
this.actor.add_style_pseudo_class('selected');
this.actor.grab_key_focus();
this.add_style_pseudo_class('selected');
this.grab_key_focus();
} else {
this.actor.remove_style_pseudo_class('selected');
this.remove_style_pseudo_class('selected');
}
}
@ -117,7 +125,7 @@ var UserListItem = class {
let startTime = GLib.get_monotonic_time();
this._timedLoginTimeoutId = GLib.timeout_add (GLib.PRIORITY_DEFAULT, 33,
this._timedLoginTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 33,
() => {
let currentTime = GLib.get_monotonic_time();
let elapsedTime = (currentTime - startTime) / GLib.USEC_PER_SEC;
@ -145,23 +153,33 @@ var UserListItem = class {
this._timedLoginIndicator.visible = false;
this._timedLoginIndicator.scale_x = 0.;
}
};
Signals.addSignalMethods(UserListItem.prototype);
});
var UserList = class {
constructor() {
this.actor = new St.ScrollView({ style_class: 'login-dialog-user-list-view' });
this.actor.set_policy(St.PolicyType.NEVER,
St.PolicyType.AUTOMATIC);
var UserList = GObject.registerClass({
Signals: {
'activate': { param_types: [UserListItem.$gtype] },
'item-added': { param_types: [UserListItem.$gtype] },
},
}, class UserList extends St.ScrollView {
_init() {
super._init({
style_class: 'login-dialog-user-list-view',
x_expand: true,
y_expand: true,
});
this.set_policy(St.PolicyType.NEVER,
St.PolicyType.AUTOMATIC);
this._box = new St.BoxLayout({ vertical: true,
style_class: 'login-dialog-user-list',
pseudo_class: 'expanded' });
this.actor.add_actor(this._box);
this.add_actor(this._box);
this._items = {};
}
this.actor.connect('key-focus-in', this._moveFocusToItems.bind(this));
vfunc_key_focus_in() {
this._moveFocusToItems();
}
_moveFocusToItems() {
@ -170,10 +188,10 @@ var UserList = class {
if (!hasItems)
return;
if (global.stage.get_key_focus() != this.actor)
if (global.stage.get_key_focus() != this)
return;
let focusSet = this.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
let focusSet = this.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
if (!focusSet) {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
this._moveFocusToItems();
@ -194,26 +212,26 @@ var UserList = class {
for (let userName in this._items) {
let item = this._items[userName];
item.actor.sync_hover();
item.sync_hover();
}
}
scrollToItem(item) {
let box = item.actor.get_allocation_box();
let box = item.get_allocation_box();
let adjustment = this.actor.get_vscroll_bar().get_adjustment();
let adjustment = this.get_vscroll_bar().get_adjustment();
let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
adjustment.ease(value, {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: _SCROLL_ANIMATION_TIME
duration: _SCROLL_ANIMATION_TIME,
});
}
jumpToItem(item) {
let box = item.actor.get_allocation_box();
let box = item.get_allocation_box();
let adjustment = this.actor.get_vscroll_bar().get_adjustment();
let adjustment = this.get_vscroll_bar().get_adjustment();
let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
@ -251,14 +269,14 @@ var UserList = class {
this.removeUser(user);
let item = new UserListItem(user);
this._box.add(item.actor, { x_fill: true });
this._box.add_child(item);
this._items[userName] = item;
item.connect('activate', this._onItemActivated.bind(this));
// Try to keep the focused item front-and-center
item.actor.connect('key-focus-in', () => this.scrollToItem(item));
item.connect('key-focus-in', () => this.scrollToItem(item));
this._moveFocusToItems();
@ -279,33 +297,37 @@ var UserList = class {
if (!item)
return;
item.actor.destroy();
item.destroy();
delete this._items[userName];
}
numItems() {
return Object.keys(this._items).length;
}
};
Signals.addSignalMethods(UserList.prototype);
});
var SessionMenuButton = class {
constructor() {
var SessionMenuButton = GObject.registerClass({
Signals: { 'session-activated': { param_types: [GObject.TYPE_STRING] } },
}, class SessionMenuButton extends St.Bin {
_init() {
let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' });
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
reactive: true,
track_hover: true,
can_focus: true,
accessible_name: _("Choose Session"),
accessible_role: Atk.Role.MENU,
child: gearIcon });
let button = new St.Button({
style_class: 'login-dialog-session-list-button',
reactive: true,
track_hover: true,
can_focus: true,
accessible_name: _("Choose Session"),
accessible_role: Atk.Role.MENU,
child: gearIcon,
});
this.actor = new St.Bin({ child: this._button });
super._init({ child: button });
this._button = button;
let side = St.Side.TOP;
let align = 0;
if (Gdm.get_session_ids().length > _MAX_BOTTOM_MENU_ITEMS) {
if (this.actor.text_direction == Clutter.TextDirection.RTL)
if (this.text_direction == Clutter.TextDirection.RTL)
side = St.Side.RIGHT;
else
side = St.Side.LEFT;
@ -384,15 +406,13 @@ var SessionMenuButton = class {
});
}
}
};
Signals.addSignalMethods(SessionMenuButton.prototype);
});
var LoginDialog = GObject.registerClass({
Signals: { 'failed': {} },
}, class LoginDialog extends St.Widget {
_init(parentActor) {
super._init({ style_class: 'login-dialog',
visible: false });
super._init({ style_class: 'login-dialog', visible: false });
this.get_accessible().set_role(Atk.Role.WINDOW);
@ -426,38 +446,35 @@ var LoginDialog = GObject.registerClass({
this.add_child(this._userSelectionBox);
this._userList = new UserList();
this._userSelectionBox.add(this._userList.actor,
{ expand: true,
x_fill: true,
y_fill: true });
this._userSelectionBox.add_child(this._userList);
this._authPrompt = new AuthPrompt.AuthPrompt(this._gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
this._authPrompt.connect('prompted', this._onPrompted.bind(this));
this._authPrompt.connect('reset', this._onReset.bind(this));
this._authPrompt.hide();
this.add_child(this._authPrompt.actor);
this.add_child(this._authPrompt);
// translators: this message is shown below the user list on the
// login screen. It can be activated to reveal an entry for
// manually entering the username.
let notListedLabel = new St.Label({ text: _("Not listed?"),
style_class: 'login-dialog-not-listed-label' });
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: notListedLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
let notListedLabel = new St.Label({
text: _("Not listed?"),
style_class: 'login-dialog-not-listed-label',
x_align: Clutter.ActorAlign.START,
});
this._notListedButton = new St.Button({
style_class: 'login-dialog-not-listed-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: notListedLabel,
reactive: true,
});
this._notListedButton.connect('clicked', this._hideUserListAskForUsernameAndBeginVerification.bind(this));
this._notListedButton.hide();
this._userSelectionBox.add(this._notListedButton,
{ expand: false,
x_align: St.Align.START,
x_fill: true });
this._userSelectionBox.add_child(this._notListedButton);
this._bannerView = new St.ScrollView({ style_class: 'login-dialog-banner-view',
opacity: 0,
@ -492,11 +509,11 @@ var LoginDialog = GObject.registerClass({
this._sessionMenuButton = new SessionMenuButton();
this._sessionMenuButton.connect('session-activated',
(list, sessionId) => {
this._greeter.call_select_session_sync (sessionId, null);
this._greeter.call_select_session_sync(sessionId, null);
});
this._sessionMenuButton.actor.opacity = 0;
this._sessionMenuButton.actor.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
this._sessionMenuButton.opacity = 0;
this._sessionMenuButton.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton);
this._disableUserList = undefined;
this._userListLoaded = false;
@ -579,8 +596,8 @@ var LoginDialog = GObject.registerClass({
let authPromptAllocation = null;
let authPromptWidth = 0;
if (this._authPrompt.actor.visible) {
authPromptAllocation = this._getCenterActorAllocation(dialogBox, this._authPrompt.actor);
if (this._authPrompt.visible) {
authPromptAllocation = this._getCenterActorAllocation(dialogBox, this._authPrompt);
authPromptWidth = authPromptAllocation.x2 - authPromptAllocation.x1;
}
@ -685,12 +702,11 @@ var LoginDialog = GObject.registerClass({
}
// Finally hand out the allocations
if (bannerAllocation) {
if (bannerAllocation)
this._bannerView.allocate(bannerAllocation, flags);
}
if (authPromptAllocation)
this._authPrompt.actor.allocate(authPromptAllocation, flags);
this._authPrompt.allocate(authPromptAllocation, flags);
if (userSelectionAllocation)
this._userSelectionBox.allocate(userSelectionAllocation, flags);
@ -760,7 +776,7 @@ var LoginDialog = GObject.registerClass({
this._bannerView.ease({
opacity: 255,
duration: _FADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
@ -794,7 +810,7 @@ var LoginDialog = GObject.registerClass({
_onPrompted() {
if (this._shouldShowSessionMenuButton()) {
this._sessionMenuButton.updateSensitivity(true);
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor);
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton);
} else {
this._sessionMenuButton.updateSensitivity(false);
}
@ -803,9 +819,9 @@ var LoginDialog = GObject.registerClass({
_resetGreeterProxy() {
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
if (this._greeter) {
if (this._greeter)
this._greeter.run_dispose();
}
this._greeter = this._gdmClient.get_greeter_sync(null);
this._defaultSessionChangedId = this._greeter.connect('default-session-name-changed',
@ -854,14 +870,14 @@ var LoginDialog = GObject.registerClass({
}
_showPrompt() {
if (this._authPrompt.actor.visible)
if (this._authPrompt.visible)
return;
this._authPrompt.actor.opacity = 0;
this._authPrompt.actor.show();
this._authPrompt.actor.ease({
this._authPrompt.opacity = 0;
this._authPrompt.show();
this._authPrompt.ease({
opacity: 255,
duration: _FADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
this._fadeInBannerView();
}
@ -929,7 +945,7 @@ var LoginDialog = GObject.registerClass({
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
this._authPrompt.reset();
this._unbindOpacity();
}
},
});
}
@ -951,7 +967,7 @@ var LoginDialog = GObject.registerClass({
onComplete: () => {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
this._unbindOpacity();
}
},
});
}
@ -968,7 +984,7 @@ var LoginDialog = GObject.registerClass({
let hold = new Batch.Hold();
let signalId = this._userList.connect('item-added',
() => {
let item = this._userList.getItemFromUserName(userName);
item = this._userList.getItemFromUserName(userName);
if (item)
hold.release();
@ -1022,9 +1038,8 @@ var LoginDialog = GObject.registerClass({
() => {
// If we're just starting out, start on the right item.
if (!this._userManager.is_loaded) {
if (!this._userManager.is_loaded)
this._userList.jumpToItem(loginItem);
}
},
() => {
@ -1045,12 +1060,12 @@ var LoginDialog = GObject.registerClass({
() => {
// If idle timeout is done, make sure the timed login indicator is shown
if (delay > _TIMED_LOGIN_IDLE_THRESHOLD &&
this._authPrompt.actor.visible)
this._authPrompt.visible)
this._authPrompt.cancel();
if (delay > _TIMED_LOGIN_IDLE_THRESHOLD || firstRun) {
this._userList.scrollToItem(loginItem);
loginItem.actor.grab_key_focus();
loginItem.grab_key_focus();
}
},
@ -1075,9 +1090,8 @@ var LoginDialog = GObject.registerClass({
// Restart timed login on user interaction
global.stage.connect('captured-event', (actor, event) => {
if (event.type() == Clutter.EventType.KEY_PRESS ||
event.type() == Clutter.EventType.BUTTON_PRESS) {
event.type() == Clutter.EventType.BUTTON_PRESS)
this._startTimedLogin(userName, seconds);
}
return Clutter.EVENT_PROPAGATE;
});
@ -1111,7 +1125,7 @@ var LoginDialog = GObject.registerClass({
this._sessionMenuButton.close();
this._setUserListExpanded(true);
this._notListedButton.show();
this._userList.actor.grab_key_focus();
this._userList.grab_key_focus();
}
_beginVerificationForItem(item) {
@ -1120,8 +1134,7 @@ var LoginDialog = GObject.registerClass({
let userName = item.user.get_user_name();
let hold = new Batch.Hold();
this._authPrompt.begin({ userName: userName,
hold: hold });
this._authPrompt.begin({ userName, hold });
return hold;
}
@ -1184,9 +1197,8 @@ var LoginDialog = GObject.registerClass({
let users = this._userManager.list_users();
for (let i = 0; i < users.length; i++) {
for (let i = 0; i < users.length; i++)
this._userList.addUser(users[i]);
}
this._updateDisableUserList();
@ -1219,7 +1231,7 @@ var LoginDialog = GObject.registerClass({
_("Login Window"),
'dialog-password-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
this._userList.actor.grab_key_focus();
this._userList.grab_key_focus();
this.show();
this.opacity = 0;
@ -1228,7 +1240,7 @@ var LoginDialog = GObject.registerClass({
this.ease({
opacity: 255,
duration: 1000,
mode: Clutter.AnimationMode.EASE_IN_QUAD
mode: Clutter.AnimationMode.EASE_IN_QUAD,
});
return true;

View File

@ -23,7 +23,7 @@ function OVirtCredentials() {
g_interface_info: OVirtCredentialsInfo,
g_name: 'org.ovirt.vdsm.Credentials',
g_object_path: '/org/ovirt/vdsm/Credentials',
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
self.init(null);
return self;
}

View File

@ -37,7 +37,7 @@ var MessageType = {
NONE: 0,
ERROR: 1,
INFO: 2,
HINT: 3
HINT: 3,
};
function fadeInActor(actor) {
@ -58,7 +58,7 @@ function fadeInActor(actor) {
onComplete: () => {
this.set_height(-1);
hold.release();
}
},
});
return hold;
@ -81,7 +81,7 @@ function fadeOutActor(actor) {
this.hide();
this.set_height(-1);
hold.release();
}
},
});
return hold;
}
@ -109,7 +109,7 @@ function cloneAndFadeOutActor(actor) {
onComplete: () => {
clone.destroy();
hold.release();
}
},
});
return hold;
}
@ -272,7 +272,7 @@ var ShellUserVerifier = class {
let interval = this._getIntervalForMessage(message);
this.hasPendingMessages = true;
this._messageQueue.push({ text: message, type: messageType, interval: interval });
this._messageQueue.push({ text: message, type: messageType, interval });
this._queueMessageTimeout();
}
@ -544,6 +544,7 @@ var ShellUserVerifier = class {
});
}
} else {
// eslint-disable-next-line no-lonely-if
if (!this.hasPendingMessages) {
this._cancelAndReset();
} else {
@ -571,9 +572,8 @@ var ShellUserVerifier = class {
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (this.serviceIsForeground(serviceName)) {
if (this.serviceIsForeground(serviceName))
this._verificationFailed(true);
}
}
};
Signals.addSignalMethods(ShellUserVerifier.prototype);

View File

@ -15,7 +15,7 @@ const Config = imports.misc.config;
var ExtensionType = {
SYSTEM: 1,
PER_USER: 2
PER_USER: 2,
};
var ExtensionState = {
@ -28,7 +28,7 @@ var ExtensionState = {
// Used as an error state for operations on unknown extensions,
// should never be in a real extensionMeta object.
UNINSTALLED: 99
UNINSTALLED: 99,
};
const SERIALIZED_PROPERTIES = ['type', 'state', 'path', 'error', 'hasPrefs', 'canChange'];
@ -36,10 +36,11 @@ const SERIALIZED_PROPERTIES = ['type', 'state', 'path', 'error', 'hasPrefs', 'ca
/**
* getCurrentExtension:
*
* Returns the current extension, or null if not called from an extension.
* @returns {?object} - The current extension, or null if not called from
* an extension.
*/
function getCurrentExtension() {
let stack = (new Error()).stack.split('\n');
let stack = new Error().stack.split('\n');
let extensionStackLine;
// Search for an occurrence of an extension stack frame
@ -84,7 +85,7 @@ function getCurrentExtension() {
/**
* initTranslations:
* @domain: (optional): the gettext domain to use
* @param {string=} domain - the gettext domain to use
*
* Initialize Gettext to load translations from extensionsdir/locale.
* If @domain is not provided, it will be taken from metadata['gettext-domain']
@ -108,7 +109,8 @@ function initTranslations(domain) {
/**
* getSettings:
* @schema: (optional): the GSettings schema id
* @param {string=} schema - the GSettings schema id
* @returns {Gio.Settings} - a new settings object for @schema
*
* Builds and returns a GSettings schema for @schema, using schema files
* in extensionsdir/schemas. If @schema is omitted, it is taken from
@ -128,12 +130,13 @@ function getSettings(schema) {
// SYSTEM extension that has been installed in the same prefix as the shell
let schemaDir = extension.dir.get_child('schemas');
let schemaSource;
if (schemaDir.query_exists(null))
if (schemaDir.query_exists(null)) {
schemaSource = GioSSS.new_from_directory(schemaDir.get_path(),
GioSSS.get_default(),
false);
else
} else {
schemaSource = GioSSS.get_default();
}
let schemaObj = schemaSource.lookup(schema, true);
if (!schemaObj)
@ -144,8 +147,9 @@ function getSettings(schema) {
/**
* versionCheck:
* @required: an array of versions we're compatible with
* @current: the version we have
* @param {string[]} required - an array of versions we're compatible with
* @param {string} current - the version we have
* @returns {bool} - true if @current is compatible with @required
*
* Check if a component is compatible for an extension.
* @required is an array, and at least one version must match.

View File

@ -1,5 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported collectFromDatadirs, deleteGFile, recursivelyDeleteDir,
/* exported collectFromDatadirs, recursivelyDeleteDir,
recursivelyMoveDir, loadInterfaceXML */
const { Gio, GLib } = imports.gi;
@ -29,11 +29,6 @@ function collectFromDatadirs(subdir, includeUserDir, processFile) {
}
}
function deleteGFile(file) {
// Work around 'delete' being a keyword in JS.
return file['delete'](null);
}
function recursivelyDeleteDir(dir, deleteParent) {
let children = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
@ -43,13 +38,13 @@ function recursivelyDeleteDir(dir, deleteParent) {
let type = info.get_file_type();
let child = dir.get_child(info.get_name());
if (type == Gio.FileType.REGULAR)
deleteGFile(child);
child.delete(null);
else if (type == Gio.FileType.DIRECTORY)
recursivelyDeleteDir(child, true);
}
if (deleteParent)
deleteGFile(dir);
dir.delete(null);
}
function recursivelyMoveDir(srcDir, destDir) {
@ -75,14 +70,14 @@ let _ifaceResource = null;
function loadInterfaceXML(iface) {
if (!_ifaceResource) {
// don't use global.datadir so the method is usable from tests/tools
let dir = GLib.getenv ('GNOME_SHELL_DATADIR') || Config.PKGDATADIR;
let path = dir + '/gnome-shell-dbus-interfaces.gresource';
let dir = GLib.getenv('GNOME_SHELL_DATADIR') || Config.PKGDATADIR;
let path = `${dir}/gnome-shell-dbus-interfaces.gresource`;
_ifaceResource = Gio.Resource.load(path);
_ifaceResource._register();
}
let xml = null;
let uri = 'resource:///org/gnome/shell/dbus-interfaces/' + iface + '.xml';
let uri = `resource:///org/gnome/shell/dbus-interfaces/${iface}.xml`;
let f = Gio.File.new_for_uri(uri);
try {

View File

@ -11,7 +11,7 @@ var PresenceStatus = {
AVAILABLE: 0,
INVISIBLE: 1,
BUSY: 2,
IDLE: 3
IDLE: 3,
};
var PresenceProxy = Gio.DBusProxy.makeProxyWrapper(PresenceIface);

View File

@ -82,11 +82,11 @@ var HistoryManager = class {
_onEntryKeyPress(entry, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Up) {
if (symbol == Clutter.KEY_Up)
return this._setPrevItem(entry.get_text());
} else if (symbol == Clutter.KEY_Down) {
else if (symbol == Clutter.KEY_Down)
return this._setNextItem(entry.get_text());
}
return Clutter.EVENT_PROPAGATE;
}

View File

@ -142,7 +142,7 @@ var IBusManager = class {
});
this._panelService.connect('focus-in', (panel, path) => {
if (!GLib.str_has_suffix(path, '/InputContext_1'))
this.emit ('focus-in');
this.emit('focus-in');
});
this._panelService.connect('focus-out', () => this.emit('focus-out'));
@ -157,10 +157,10 @@ var IBusManager = class {
} catch (e) {
}
// If an engine is already active we need to get its properties
this._ibus.get_global_engine_async(-1, this._cancellable, (_bus, result) => {
this._ibus.get_global_engine_async(-1, this._cancellable, (_bus, res) => {
let engine;
try {
engine = this._ibus.get_global_engine_async_finish(result);
engine = this._ibus.get_global_engine_async_finish(res);
if (!engine)
return;
} catch (e) {

View File

@ -48,7 +48,7 @@ class InputMethod extends Clutter.InputMethod {
_onConnected() {
this._cancellable = new Gio.Cancellable();
this._ibus.create_input_context_async ('gnome-shell', -1,
this._ibus.create_input_context_async('gnome-shell', -1,
this._cancellable, this._setContext.bind(this));
}
@ -69,6 +69,7 @@ class InputMethod extends Clutter.InputMethod {
this._context.connect('show-preedit-text', this._onShowPreeditText.bind(this));
this._context.connect('hide-preedit-text', this._onHidePreeditText.bind(this));
this._context.connect('forward-key-event', this._onForwardKeyEvent.bind(this));
this._context.connect('destroy', this._clear.bind(this));
this._updateCapabilities();
}
@ -128,7 +129,7 @@ class InputMethod extends Clutter.InputMethod {
_onForwardKeyEvent(_context, keyval, keycode, state) {
let press = (state & IBus.ModifierType.RELEASE_MASK) == 0;
state &= ~(IBus.ModifierType.RELEASE_MASK);
state &= ~IBus.ModifierType.RELEASE_MASK;
let curEvent = Clutter.get_current_event();
let time;
@ -264,6 +265,9 @@ class InputMethod extends Clutter.InputMethod {
event.get_key_code() - 8, // Convert XKB keycodes to evcodes
state, -1, this._cancellable,
(context, res) => {
if (context != this._context)
return;
try {
let retval = context.process_key_event_async_finish(res);
this.notify_key_event(event, retval);

View File

@ -40,6 +40,15 @@ var IntrospectService = class {
});
this._syncRunningApplications();
this._whitelistMap = new Map();
APP_WHITELIST.forEach(appName => {
Gio.DBus.watch_name(Gio.BusType.SESSION,
appName,
Gio.BusNameWatcherFlags.NONE,
(conn, name, owner) => this._whitelistMap.set(name, owner),
(conn, name) => this._whitelistMap.delete(name));
});
}
_isStandaloneApp(app) {
@ -51,7 +60,7 @@ var IntrospectService = class {
}
_isSenderWhitelisted(sender) {
return APP_WHITELIST.includes(sender);
return [...this._whitelistMap.values()].includes(sender);
}
_getSandboxedAppId(app) {
@ -70,7 +79,7 @@ var IntrospectService = class {
for (let app of apps) {
let appInfo = {};
let isAppActive = (focusedApp == app);
let isAppActive = focusedApp == app;
if (!this._isStandaloneApp(app))
continue;
@ -104,10 +113,10 @@ var IntrospectService = class {
return false;
let type = window.get_window_type();
return (type == Meta.WindowType.NORMAL ||
return type == Meta.WindowType.NORMAL ||
type == Meta.WindowType.DIALOG ||
type == Meta.WindowType.MODAL_DIALOG ||
type == Meta.WindowType.UTILITY);
type == Meta.WindowType.UTILITY;
}
GetRunningApplicationsAsync(params, invocation) {
@ -152,9 +161,9 @@ var IntrospectService = class {
'app-id': GLib.Variant.new('s', app.get_id()),
'client-type': GLib.Variant.new('u', window.get_client_type()),
'is-hidden': GLib.Variant.new('b', window.is_hidden()),
'has-focus': GLib.Variant.new('b', (window == focusWindow)),
'has-focus': GLib.Variant.new('b', window == focusWindow),
'width': GLib.Variant.new('u', frameRect.width),
'height': GLib.Variant.new('u', frameRect.height)
'height': GLib.Variant.new('u', frameRect.height),
};
// These properties may not be available for all windows:
@ -164,9 +173,10 @@ var IntrospectService = class {
if (wmClass != null)
windowsList[windowId]['wm-class'] = GLib.Variant.new('s', wmClass);
if (sandboxedAppId != null)
if (sandboxedAppId != null) {
windowsList[windowId]['sandboxed-app-id'] =
GLib.Variant.new('s', sandboxedAppId);
}
}
}
invocation.return_value(new GLib.Variant('(a{ta{sv}})', [windowsList]));

View File

@ -11,9 +11,8 @@ function getCompletions(text, commandHeader, globalCompletionList) {
let methods = [];
let expr_, base;
let attrHead = '';
if (globalCompletionList == null) {
if (globalCompletionList == null)
globalCompletionList = [];
}
let offset = getExpressionOffset(text, text.length - 1);
if (offset >= 0) {
@ -59,9 +58,8 @@ function isStopChar(c) {
function findMatchingQuote(expr, offset) {
let quoteChar = expr.charAt(offset);
for (let i = offset - 1; i >= 0; --i) {
if (expr.charAt(i) == quoteChar && expr.charAt(i - 1) != '\\') {
if (expr.charAt(i) == quoteChar && expr.charAt(i - 1) != '\\')
return i;
}
}
return -1;
}
@ -69,9 +67,8 @@ function findMatchingQuote(expr, offset) {
// Given the ending position of a regex, find where it starts
function findMatchingSlash(expr, offset) {
for (let i = offset - 1; i >= 0; --i) {
if (expr.charAt(i) == '/' && expr.charAt(i - 1) != '\\') {
if (expr.charAt(i) == '/' && expr.charAt(i - 1) != '\\')
return i;
}
}
return -1;
}
@ -82,31 +79,30 @@ function findMatchingSlash(expr, offset) {
// findMatchingBrace("[(])", 3) returns 1.
function findMatchingBrace(expr, offset) {
let closeBrace = expr.charAt(offset);
let openBrace = ({ ')': '(', ']': '[' })[closeBrace];
let openBrace = { ')': '(', ']': '[' }[closeBrace];
function findTheBrace(expr, offset) {
if (offset < 0) {
return -1;
}
return findTheBrace(expr, offset - 1, openBrace, closeBrace);
}
if (expr.charAt(offset) == openBrace) {
return offset;
}
if (expr.charAt(offset).match(/['"]/)) {
return findTheBrace(expr, findMatchingQuote(expr, offset) - 1);
}
if (expr.charAt(offset) == '/') {
return findTheBrace(expr, findMatchingSlash(expr, offset) - 1);
}
if (expr.charAt(offset) == closeBrace) {
return findTheBrace(expr, findTheBrace(expr, offset - 1) - 1);
}
function findTheBrace(expr, offset, ...braces) {
let [openBrace, closeBrace] = braces;
return findTheBrace(expr, offset - 1);
if (offset < 0)
return -1;
}
if (expr.charAt(offset) == openBrace)
return offset;
return findTheBrace(expr, offset - 1);
if (expr.charAt(offset).match(/['"]/))
return findTheBrace(expr, findMatchingQuote(expr, offset) - 1, ...braces);
if (expr.charAt(offset) == '/')
return findTheBrace(expr, findMatchingSlash(expr, offset) - 1, ...braces);
if (expr.charAt(offset) == closeBrace)
return findTheBrace(expr, findTheBrace(expr, offset - 1, ...braces) - 1, ...braces);
return findTheBrace(expr, offset - 1, ...braces);
}
// Walk expr backwards from offset looking for the beginning of an
@ -118,13 +114,11 @@ function getExpressionOffset(expr, offset) {
while (offset >= 0) {
let currChar = expr.charAt(offset);
if (isStopChar(currChar)) {
if (isStopChar(currChar))
return offset + 1;
}
if (currChar.match(/[)\]]/)) {
if (currChar.match(/[)\]]/))
offset = findMatchingBrace(expr, offset);
}
--offset;
}
@ -141,10 +135,10 @@ function isValidPropertyName(w) {
// To get all properties (enumerable and not), we need to walk
// the prototype chain ourselves
function getAllProps(obj) {
if (obj === null || obj === undefined) {
if (obj === null || obj === undefined)
return [];
}
return Object.getOwnPropertyNames(obj).concat( getAllProps(Object.getPrototypeOf(obj)) );
return Object.getOwnPropertyNames(obj).concat(getAllProps(Object.getPrototypeOf(obj)));
}
// Given a string _expr_, returns all methods
@ -168,7 +162,7 @@ function getPropertyNamesFromExpression(expr, commandHeader = '') {
if (typeof obj === 'object') {
let allProps = getAllProps(obj);
// Get only things we are allowed to complete following a '.'
allProps = allProps.filter( isValidPropertyName );
allProps = allProps.filter(isValidPropertyName);
// Make sure propsUnique contains one key for every
// property so we end up with a unique list of properties
@ -189,24 +183,26 @@ function getCommonPrefix(words) {
return word;
}
// Remove any blocks that are quoted or are in a regex
function removeLiterals(str) {
if (str.length == 0)
return '';
let currChar = str.charAt(str.length - 1);
if (currChar == '"' || currChar == '\'') {
return removeLiterals(
str.slice(0, findMatchingQuote(str, str.length - 1)));
} else if (currChar == '/') {
return removeLiterals(
str.slice(0, findMatchingSlash(str, str.length - 1)));
}
return removeLiterals(str.slice(0, str.length - 1)) + currChar;
}
// Returns true if there is reason to think that eval(str)
// will modify the global scope
function isUnsafeExpression(str) {
// Remove any blocks that are quoted or are in a regex
function removeLiterals(str) {
if (str.length == 0) {
return '';
}
let currChar = str.charAt(str.length - 1);
if (currChar == '"' || currChar == '\'') {
return removeLiterals(str.slice(0, findMatchingQuote(str, str.length - 1)));
} else if (currChar == '/') {
return removeLiterals(str.slice(0, findMatchingSlash(str, str.length - 1)));
}
return removeLiterals(str.slice(0, str.length - 1)) + currChar;
}
// Check for any sort of assignment
// The strategy used is dumb: remove any quotes
@ -214,8 +210,8 @@ function isUnsafeExpression(str) {
// If there is, it might be an unsafe assignment.
let prunedStr = removeLiterals(str);
prunedStr = prunedStr.replace(/[=!]==/g, ''); //replace === and !== with nothing
prunedStr = prunedStr.replace(/[=<>!]=/g, ''); //replace ==, <=, >=, != with nothing
prunedStr = prunedStr.replace(/[=!]==/g, ''); // replace === and !== with nothing
prunedStr = prunedStr.replace(/[=<>!]=/g, ''); // replace ==, <=, >=, != with nothing
if (prunedStr.match(/[=]/)) {
return true;

View File

@ -74,8 +74,9 @@ function registerSessionWithGDM() {
let _loginManager = null;
/**
* LoginManager:
* getLoginManager:
* An abstraction over systemd/logind and ConsoleKit.
* @returns {object} - the LoginManager singleton
*
*/
function getLoginManager() {
@ -103,21 +104,21 @@ var LoginManagerSystemd = class {
getCurrentSessionProxy(callback) {
if (this._currentSession) {
callback (this._currentSession);
callback(this._currentSession);
return;
}
let sessionId = GLib.getenv('XDG_SESSION_ID');
if (!sessionId) {
log('Unset XDG_SESSION_ID, getCurrentSessionProxy() called outside a user session. Asking logind directly.');
let [session, objectPath_] = this._userProxy.Display;
let [session, objectPath] = this._userProxy.Display;
if (session) {
log(`Will monitor session ${session}`);
sessionId = session;
} else {
log('Failed to find "Display" session; are we the greeter?');
for (let [session, objectPath] of this._userProxy.Sessions) {
for ([session, objectPath] of this._userProxy.Sessions) {
let sessionProxy = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
objectPath);
@ -185,7 +186,7 @@ var LoginManagerSystemd = class {
try {
let [outVariant_, fdList] = proxy.call_with_unix_fd_list_finish(result);
fd = fdList.steal_fds()[0];
callback(new Gio.UnixInputStream({ fd: fd }));
callback(new Gio.UnixInputStream({ fd }));
} catch (e) {
logError(e, "Error getting systemd inhibitor");
callback(null);

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ModemBase, ModemGsm, ModemCdma, BroadbandModem */
const { Gio, NMA } = imports.gi;
const Signals = imports.signals;
const { Gio, GObject, NM, NMA } = imports.gi;
const { loadInterfaceXML } = imports.misc.fileUtils;
@ -98,21 +98,46 @@ const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInter
const ModemCdmaInterface = loadInterfaceXML('org.freedesktop.ModemManager.Modem.Cdma');
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
var ModemGsm = class {
constructor(path) {
this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
var ModemBase = GObject.registerClass({
GTypeFlags: GObject.TypeFlags.ABSTRACT,
Properties: {
'operator-name': GObject.ParamSpec.string(
'operator-name', 'operator-name', 'operator-name',
GObject.ParamFlags.READABLE,
null),
'signal-quality': GObject.ParamSpec.int(
'signal-quality', 'signal-quality', 'signal-quality',
GObject.ParamFlags.READABLE,
0, 100, 0),
},
}, class ModemBase extends GObject.Object {
_setOperatorName(operatorName) {
if (this.operator_name == operatorName)
return;
this.operator_name = operatorName;
this.notify('operator-name');
}
this.signal_quality = 0;
this.operator_name = null;
_setSignalQuality(signalQuality) {
if (this.signal_quality == signalQuality)
return;
this.signal_quality = signalQuality;
this.notify('signal-quality');
}
});
var ModemGsm = GObject.registerClass(
class ModemGsm extends ModemBase {
_init(path) {
super._init();
this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
// Code is duplicated because the function have different signatures
this._proxy.connectSignal('SignalQuality', (proxy, sender, [quality]) => {
this.signal_quality = quality;
this.emit('notify::signal-quality');
this._setSignalQuality(quality);
});
this._proxy.connectSignal('RegistrationInfo', (proxy, sender, [_status, code, name]) => {
this.operator_name = _findProviderForMccMnc(name, code);
this.emit('notify::operator-name');
this._setOperatorName(_findProviderForMccMnc(name, code));
});
this._proxy.GetRegistrationInfoRemote(([result], err) => {
if (err) {
@ -121,32 +146,28 @@ var ModemGsm = class {
}
let [status_, code, name] = result;
this.operator_name = _findProviderForMccMnc(name, code);
this.emit('notify::operator-name');
this._setOperatorName(_findProviderForMccMnc(name, code));
});
this._proxy.GetSignalQualityRemote((result, err) => {
if (err) {
// it will return an error if the device is not connected
this.signal_quality = 0;
this._setSignalQuality(0);
} else {
let [quality] = result;
this.signal_quality = quality;
this._setSignalQuality(quality);
}
this.emit('notify::signal-quality');
});
}
};
Signals.addSignalMethods(ModemGsm.prototype);
});
var ModemCdma = class {
constructor(path) {
var ModemCdma = GObject.registerClass(
class ModemCdma extends ModemBase {
_init(path) {
super._init();
this._proxy = new ModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
this.signal_quality = 0;
this.operator_name = null;
this._proxy.connectSignal('SignalQuality', (proxy, sender, params) => {
this.signal_quality = params[0];
this.emit('notify::signal-quality');
this._setSignalQuality(params[0]);
// receiving this signal means the device got activated
// and we can finally call GetServingSystem
@ -156,12 +177,11 @@ var ModemCdma = class {
this._proxy.GetSignalQualityRemote((result, err) => {
if (err) {
// it will return an error if the device is not connected
this.signal_quality = 0;
this._setSignalQuality(0);
} else {
let [quality] = result;
this.signal_quality = quality;
this._setSignalQuality(quality);
}
this.emit('notify::signal-quality');
});
}
@ -169,17 +189,14 @@ var ModemCdma = class {
this._proxy.GetServingSystemRemote(([result], err) => {
if (err) {
// it will return an error if the device is not connected
this.operator_name = null;
this._setOperatorName(null);
} else {
let [bandClass_, band_, sid] = result;
this.operator_name = _findProviderForSid(sid);
this._setOperatorName(_findProviderForSid(sid));
}
this.emit('notify::operator-name');
});
}
};
Signals.addSignalMethods(ModemCdma.prototype);
});
// ------------------------------------------------------- //
@ -195,12 +212,20 @@ const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gp
const BroadbandModemCdmaInterface = loadInterfaceXML('org.freedesktop.ModemManager1.Modem.ModemCdma');
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
var BroadbandModem = class {
constructor(path, capabilities) {
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
var BroadbandModem = GObject.registerClass({
Properties: {
'capabilities': GObject.ParamSpec.flags(
'capabilities', 'capabilities', 'capabilities',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
NM.DeviceModemCapabilities.$gtype,
NM.DeviceModemCapabilities.NONE),
},
}, class BroadbandModem extends ModemBase {
_init(path, capabilities) {
super._init({ capabilities });
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._capabilities = capabilities;
this._proxy.connect('g-properties-changed', (proxy, properties) => {
if ('SignalQuality' in properties.deep_unpack())
@ -224,9 +249,8 @@ var BroadbandModem = class {
}
_reloadSignalQuality() {
let [quality, recent_] = this._proxy.SignalQuality;
this.signal_quality = quality;
this.emit('notify::signal-quality');
let [quality, recent_] = this.SignalQuality;
this._setSignalQuality(quality);
}
_reloadOperatorName() {
@ -240,8 +264,7 @@ var BroadbandModem = class {
newName += this.operator_name_cdma;
}
this.operator_name = newName;
this.emit('notify::operator-name');
this._setOperatorName(newName);
}
_reload3gppOperatorName() {
@ -256,5 +279,4 @@ var BroadbandModem = class {
this.operator_name_cdma = _findProviderForSid(sid);
this._reloadOperatorName();
}
};
Signals.addSignalMethods(BroadbandModem.prototype);
});

View File

@ -192,9 +192,8 @@ var ObjectManager = class {
_onNameAppeared() {
this._managerProxy.GetManagedObjectsRemote((result, error) => {
if (!result) {
if (error) {
if (error)
logError(error, `could not get remote objects for service ${this._serviceName} path ${this._managerPath}`);
}
this._tryToCompleteLoad();
return;

View File

@ -17,9 +17,10 @@
// @params and @defaults
function parse(params = {}, defaults, allowExtras) {
if (!allowExtras) {
for (let prop in params)
for (let prop in params) {
if (!(prop in defaults))
throw new Error(`Unrecognized parameter "${prop}"`);
}
}
let defaultsCopy = Object.assign({}, defaults);

View File

@ -72,11 +72,10 @@ var SmartcardManager = class {
if ('IsInserted' in properties.deep_unpack()) {
this._updateToken(token);
if (token.IsInserted) {
if (token.IsInserted)
this.emit('smartcard-inserted', token);
} else {
else
this.emit('smartcard-removed', token);
}
}
});

View File

@ -74,8 +74,8 @@ const SystemActions = GObject.registerClass({
'orientation-lock-icon',
'orientation-lock-icon',
GObject.ParamFlags.READWRITE,
null)
}
null),
},
}, class SystemActions extends GObject.Object {
_init() {
super._init();
@ -90,7 +90,7 @@ const SystemActions = GObject.registerClass({
iconName: 'system-shutdown-symbolic',
// Translators: A list of keywords that match the power-off action, separated by semicolons
keywords: _("power off;shutdown;reboot;restart").split(/[; ]/),
available: false
available: false,
});
this._actions.set(LOCK_SCREEN_ACTION_ID, {
// Translators: The name of the lock screen action in search
@ -98,7 +98,7 @@ const SystemActions = GObject.registerClass({
iconName: 'system-lock-screen-symbolic',
// Translators: A list of keywords that match the lock screen action, separated by semicolons
keywords: _("lock screen").split(/[; ]/),
available: false
available: false,
});
this._actions.set(LOGOUT_ACTION_ID, {
// Translators: The name of the logout action in search
@ -106,7 +106,7 @@ const SystemActions = GObject.registerClass({
iconName: 'application-exit-symbolic',
// Translators: A list of keywords that match the logout action, separated by semicolons
keywords: _("logout;log out;sign off").split(/[; ]/),
available: false
available: false,
});
this._actions.set(SUSPEND_ACTION_ID, {
// Translators: The name of the suspend action in search
@ -114,7 +114,7 @@ const SystemActions = GObject.registerClass({
iconName: 'media-playback-pause-symbolic',
// Translators: A list of keywords that match the suspend action, separated by semicolons
keywords: _("suspend;sleep").split(/[; ]/),
available: false
available: false,
});
this._actions.set(SWITCH_USER_ACTION_ID, {
// Translators: The name of the switch user action in search
@ -122,7 +122,7 @@ const SystemActions = GObject.registerClass({
iconName: 'system-switch-user-symbolic',
// Translators: A list of keywords that match the switch user action, separated by semicolons
keywords: _("switch user").split(/[; ]/),
available: false
available: false,
});
this._actions.set(LOCK_ORIENTATION_ACTION_ID, {
// Translators: The name of the lock orientation action in search
@ -130,7 +130,7 @@ const SystemActions = GObject.registerClass({
iconName: '',
// Translators: A list of keywords that match the lock orientation action, separated by semicolons
keywords: _("lock orientation;screen;rotation").split(/[; ]/),
available: false
available: false,
});
this._loginScreenSettings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA });
@ -233,9 +233,10 @@ const SystemActions = GObject.registerClass({
_updateOrientationLock() {
let available = false;
if (this._sensorProxy.g_name_owner)
if (this._sensorProxy.g_name_owner) {
available = this._sensorProxy.HasAccelerometer &&
this._monitorManager.get_is_builtin_display_on();
}
this._actions.get(LOCK_ORIENTATION_ACTION_ID).available = available;
@ -273,9 +274,10 @@ const SystemActions = GObject.registerClass({
let results = [];
for (let [key, { available, keywords }] of this._actions)
for (let [key, { available, keywords }] of this._actions) {
if (available && terms.every(t => keywords.some(k => k.startsWith(t))))
results.push(key);
}
return results;
}

View File

@ -1,9 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnCommandLine,
formatTime, formatTimeSpan, createTimeLabel, insertSorted,
makeCloseButton, ensureActorVisibleInScrollView */
makeCloseButton, ensureActorVisibleInScrollView, wiggle */
const { Clutter, Gio, GLib, GObject, Shell, St } = imports.gi;
const { Clutter, Gio, GLib, GObject, Shell, St, GnomeDesktop } = imports.gi;
const Gettext = imports.gettext;
const Main = imports.ui.main;
@ -121,12 +121,15 @@ function trySpawn(argv) {
// We are only interested in the part in the parentheses. (And
// we can't pattern match the text, since it gets localized.)
let message = err.message.replace(/.*\((.+)\)/, '$1');
throw new (err.constructor)({ code: err.code,
message: message });
throw new err.constructor({ code: err.code, message });
} else {
throw err;
}
}
// Async call, we don't need the reply though
GnomeDesktop.start_systemd_scope(argv[0], pid, null, null, null, () => {});
// Dummy child watch; we don't want to double-fork internally
// because then we lose the parent-child relationship, which
// can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
@ -172,23 +175,28 @@ function formatTimeSpan(date) {
if (minutesAgo < 5)
return _("Just now");
if (hoursAgo < 1)
if (hoursAgo < 1) {
return Gettext.ngettext("%d minute ago",
"%d minutes ago", minutesAgo).format(minutesAgo);
if (daysAgo < 1)
}
if (daysAgo < 1) {
return Gettext.ngettext("%d hour ago",
"%d hours ago", hoursAgo).format(hoursAgo);
}
if (daysAgo < 2)
return _("Yesterday");
if (daysAgo < 15)
if (daysAgo < 15) {
return Gettext.ngettext("%d day ago",
"%d days ago", daysAgo).format(daysAgo);
if (weeksAgo < 8)
}
if (weeksAgo < 8) {
return Gettext.ngettext("%d week ago",
"%d weeks ago", weeksAgo).format(weeksAgo);
if (yearsAgo < 1)
}
if (yearsAgo < 1) {
return Gettext.ngettext("%d month ago",
"%d months ago", monthsAgo).format(monthsAgo);
}
return Gettext.ngettext("%d year ago",
"%d years ago", yearsAgo).format(yearsAgo);
}
@ -213,7 +221,10 @@ function formatTime(time, params) {
_desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
let clockFormat = _desktopSettings.get_string('clock-format');
params = Params.parse(params, { timeOnly: false });
params = Params.parse(params, {
timeOnly: false,
ampm: true,
});
if (clockFormat == '24h') {
// Show only the time if date is on today
@ -246,7 +257,7 @@ function formatTime(time, params) {
format = N_("%B %-d %Y, %H\u2236%M");
} else {
// Show only the time if date is on today
if (daysAgo < 1 || params.timeOnly)
if (daysAgo < 1 || params.timeOnly) // eslint-disable-line no-lonely-if
/* Translators: Time in 12h format */
format = N_("%l\u2236%M %p");
// Show the word "Yesterday" and time if date is on yesterday
@ -275,6 +286,11 @@ function formatTime(time, params) {
format = N_("%B %-d %Y, %l\u2236%M %p");
}
// Time in short 12h format, without the equivalent of "AM" or "PM"; used
// when it is clear from the context
if (!params.ampm)
format = format.replace(/\s*%p/g, '');
let formattedTime = date.format(Shell.util_translate_time_string(format));
// prepend LTR-mark to colon/ratio to force a text direction on times
return formattedTime.replace(/([:\u2236])/g, '\u200e$1');
@ -325,7 +341,7 @@ function lowerBound(array, val, cmp) {
max = mid;
}
return (min == max || cmp(array[min], val) < 0) ? max : min;
return min == max || cmp(array[min], val) < 0 ? max : min;
}
// insertSorted:
@ -346,19 +362,13 @@ function insertSorted(array, val, cmp) {
var CloseButton = GObject.registerClass(
class CloseButton extends St.Button {
_init(boxpointer) {
super._init({ style_class: 'notification-close' });
// This is a bit tricky. St.Bin has its own x-align/y-align properties
// that compete with Clutter's properties. This should be fixed for
// Clutter 2.0. Since St.Bin doesn't define its own setters, the
// setters are a workaround to get Clutter's version.
this.set_x_align(Clutter.ActorAlign.END);
this.set_y_align(Clutter.ActorAlign.START);
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
// to respect the alignments.
this.set_x_expand(true);
this.set_y_expand(true);
super._init({
style_class: 'notification-close',
x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.START,
});
this._boxPointer = boxpointer;
if (boxpointer)
@ -411,7 +421,7 @@ function ensureActorVisibleInScrollView(scrollView, actor) {
if (!parent)
throw new Error("actor not in scroll view");
let box = parent.get_allocation_box();
box = parent.get_allocation_box();
y1 += box.y1;
y2 += box.y1;
parent = parent.get_parent();
@ -426,6 +436,40 @@ function ensureActorVisibleInScrollView(scrollView, actor) {
adjustment.ease(value, {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: SCROLL_TIME
duration: SCROLL_TIME,
});
}
function wiggle(actor, params) {
params = Params.parse(params, {
offset: 0,
duration: 0,
wiggleCount: 0,
});
actor.translation_x = 0;
// Accelerate before wiggling
actor.ease({
translation_x: -params.offset,
duration: params.duration,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
// Wiggle
actor.ease({
translation_x: params.offset,
duration: params.duration,
mode: Clutter.AnimationMode.LINEAR,
repeatCount: params.wiggleCount,
autoReverse: true,
onComplete: () => {
// Decelerate and return to the original position
actor.ease({
translation_x: 0,
duration: params.duration,
mode: Clutter.AnimationMode.EASE_IN_QUAD,
});
},
});
},
});
}

View File

@ -32,6 +32,7 @@ var WeatherClient = class {
this._gclueStarting = false;
this._gclueLocationChangedId = 0;
this._needsAuth = true;
this._weatherAuthorized = false;
this._permStore = new PermissionStore.PermissionStore((proxy, error) => {
if (error) {
@ -47,11 +48,11 @@ var WeatherClient = class {
return;
}
this._permStore.LookupRemote('gnome', 'geolocation', (res, error) => {
if (error)
log(`Error looking up permission: ${error.message}`);
this._permStore.LookupRemote('gnome', 'geolocation', (res, err) => {
if (err)
log(`Error looking up permission: ${err.message}`);
let [perms, data] = error ? [{}, null] : res;
let [perms, data] = err ? [{}, null] : res;
let params = ['gnome', 'geolocation', false, data, perms];
this._onPermStoreChanged(this._permStore, '', params);
});
@ -90,7 +91,7 @@ var WeatherClient = class {
this._onWeatherProxyReady.bind(this));
this._settings = new Gio.Settings({
schema_id: 'org.gnome.shell.weather'
schema_id: 'org.gnome.shell.weather',
});
this._settings.connect('changed::automatic-location',
this._onAutomaticLocationChanged.bind(this));
@ -142,7 +143,7 @@ var WeatherClient = class {
get _useAutoLocation() {
return this._autoLocationRequested &&
this._locationSettings.get_boolean('enabled') &&
this._weatherAuthorized;
(!this._needsAuth || this._weatherAuthorized);
}
_onWeatherProxyReady(o, res) {
@ -169,12 +170,19 @@ var WeatherClient = class {
}
_onInstalledChanged() {
let hadApp = (this._weatherApp != null);
let hadApp = this._weatherApp != null;
this._weatherApp = this._appSystem.lookup_app(WEATHER_APP_ID);
let haveApp = (this._weatherApp != null);
let haveApp = this._weatherApp != null;
if (hadApp !== haveApp)
this.emit('changed');
let neededAuth = this._needsAuth;
this._needsAuth = this._weatherApp === null ||
this._weatherApp.app_info.has_key('X-Flatpak');
if (neededAuth !== this._needsAuth)
this._updateAutoLocation();
}
_loadInfo() {
@ -205,7 +213,7 @@ var WeatherClient = class {
this._weatherInfo.abort();
this._weatherInfo.set_location(location);
this._locationValid = (location != null);
this._locationValid = location != null;
this._weatherInfo.set_enabled_providers(location ? this._providers : 0);

View File

@ -57,7 +57,7 @@ var METRICS = {
units: "us" },
applicationsShowTimeSubsequent:
{ description: "Time to switch to applications view, second time",
units: "us" }
units: "us" },
};
let WINDOW_CONFIGS = [
@ -67,7 +67,7 @@ let WINDOW_CONFIGS = [
{ width: 640, height: 480, alpha: false, maximized: true, count: 5, metric: 'overviewFps5Maximized' },
{ width: 640, height: 480, alpha: false, maximized: true, count: 10, metric: 'overviewFps10Maximized' },
{ width: 640, height: 480, alpha: true, maximized: false, count: 5, metric: 'overviewFps5Alpha' },
{ width: 640, height: 480, alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha' }
{ width: 640, height: 480, alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha' },
];
function *run() {
@ -94,11 +94,12 @@ function *run() {
let config = WINDOW_CONFIGS[i / 2];
yield Scripting.destroyTestWindows();
for (let k = 0; k < config.count; k++)
for (let k = 0; k < config.count; k++) {
yield Scripting.createTestWindow({ width: config.width,
height: config.height,
alpha: config.alpha,
maximized: config.maximized });
}
yield Scripting.waitTestWindows();
yield Scripting.sleep(1000);
@ -127,11 +128,11 @@ function *run() {
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart');
// eslint-disable-next-line require-atomic-updates
Main.overview._dash.showAppsButton.checked = true;
Main.overview.dash.showAppsButton.checked = true;
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
// eslint-disable-next-line require-atomic-updates
Main.overview._dash.showAppsButton.checked = false;
Main.overview.dash.showAppsButton.checked = false;
yield Scripting.waitLeisure();
}
}
@ -174,11 +175,10 @@ function script_applicationsShowDone(time) {
}
function script_afterShowHide(_time) {
if (overviewShowCount == 1) {
if (overviewShowCount == 1)
METRICS.usedAfterOverview.value = mallocUsedSize;
} else {
else
METRICS.leakedAfterOverview.value = mallocUsedSize - METRICS.usedAfterOverview.value;
}
}
function malloc_usedSize(time, bytes) {

View File

@ -114,7 +114,7 @@ function *run() {
Scripting.scriptEvent('desktopShown');
let interfaceSettings = new Gio.Settings({
schema_id: 'org.gnome.desktop.interface'
schema_id: 'org.gnome.desktop.interface',
});
interfaceSettings.set_boolean('enable-animations', false);
@ -127,7 +127,7 @@ function *run() {
Scripting.scriptEvent('applicationsShowStart');
// eslint-disable-next-line require-atomic-updates
Main.overview._dash.showAppsButton.checked = true;
Main.overview.dash.showAppsButton.checked = true;
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');

View File

@ -11,17 +11,17 @@ const { loadInterfaceXML } = imports.misc.fileUtils;
const PortalHelperResult = {
CANCELLED: 0,
COMPLETED: 1,
RECHECK: 2
RECHECK: 2,
};
const PortalHelperSecurityLevel = {
NOT_YET_DETERMINED: 0,
SECURE: 1,
INSECURE: 2
INSECURE: 2,
};
const CONNECTIVITY_CHECK_HOST = 'nmcheck.gnome.org';
const CONNECTIVITY_CHECK_URI = 'http://' + CONNECTIVITY_CHECK_HOST;
const CONNECTIVITY_CHECK_URI = `http://${CONNECTIVITY_CHECK_HOST}`;
const CONNECTIVITY_RECHECK_RATELIMIT_TIMEOUT = 30 * GLib.USEC_PER_SEC;
const HelperDBusInterface = loadInterfaceXML('org.gnome.Shell.PortalHelper');
@ -92,7 +92,7 @@ class PortalHeaderBar extends Gtk.HeaderBar {
var PortalWindow = GObject.registerClass(
class PortalWindow extends Gtk.ApplicationWindow {
_init(application, url, timestamp, doneCallback) {
super._init({ application: application });
super._init({ application });
this.connect('delete-event', this.destroyWindow.bind(this));
this._headerBar = new PortalHeaderBar();
@ -287,7 +287,7 @@ class WebPortalHelper extends Gtk.Application {
}
Authenticate(connection, url, timestamp) {
this._queue.push({ connection: connection, url: url, timestamp: timestamp });
this._queue.push({ connection, url, timestamp });
this._processQueue();
}

View File

@ -13,7 +13,7 @@ const AccessIface = loadInterfaceXML('org.freedesktop.impl.portal.Access');
var DialogResponse = {
OK: 0,
CANCEL: 1,
CLOSED: 2
CLOSED: 2,
};
var AccessDialog = GObject.registerClass(
@ -35,7 +35,7 @@ class AccessDialog extends ModalDialog.ModalDialog {
_buildLayout(title, subtitle, body, options) {
// No support for non-modal system dialogs, so ignore the option
//let modal = options['modal'] || true;
// let modal = options['modal'] || true;
let denyLabel = options['deny_label'] || _("Deny Access");
let grantLabel = options['grant_label'] || _("Grant Access");
let iconName = options['icon'] || null;
@ -56,8 +56,8 @@ class AccessDialog extends ModalDialog.ModalDialog {
let check = new CheckBox.CheckBox();
check.getLabelActor().text = name;
check.actor.checked = selected == "true";
content.insertBeforeBody(check.actor);
check.checked = selected == "true";
content.insertBeforeBody(check);
this._choices.set(id, check);
}
@ -99,7 +99,7 @@ class AccessDialog extends ModalDialog.ModalDialog {
let results = {};
if (response == DialogResponse.OK) {
for (let [id, check] of this._choices) {
let checked = check.actor.checked ? 'true' : 'false';
let checked = check.checked ? 'true' : 'false';
results[id] = new GLib.Variant('s', checked);
}
}

View File

@ -62,7 +62,7 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
this.thumbnailsVisible = false;
let apps = Shell.AppSystem.get_default().get_running ();
let apps = Shell.AppSystem.get_default().get_running();
if (apps.length == 0)
return;
@ -111,14 +111,12 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
_initialSelection(backward, binding) {
if (binding == 'switch-group') {
if (backward) {
if (backward)
this._select(0, this._items[0].cachedWindows.length - 1);
} else {
if (this._items[0].cachedWindows.length > 1)
this._select(0, 1);
else
this._select(0, 0);
}
else if (this._items[0].cachedWindows.length > 1)
this._select(0, 1);
else
this._select(0, 0);
} else if (binding == 'switch-group-backward') {
this._select(0, this._items[0].cachedWindows.length - 1);
} else if (binding == 'switch-applications-backward') {
@ -180,28 +178,27 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
this._select(this._next());
} else if (action == Meta.KeyBindingAction.SWITCH_APPLICATIONS_BACKWARD) {
this._select(this._previous());
} else if (keysym == Clutter.q) {
} else if (keysym === Clutter.KEY_q) {
this._quitApplication(this._selectedIndex);
} else if (this._thumbnailsFocused) {
if (keysym == Clutter.Left)
if (keysym === Clutter.KEY_Left)
this._select(this._selectedIndex, this._previousWindow());
else if (keysym == Clutter.Right)
else if (keysym === Clutter.KEY_Right)
this._select(this._selectedIndex, this._nextWindow());
else if (keysym == Clutter.Up)
else if (keysym === Clutter.KEY_Up)
this._select(this._selectedIndex, null, true);
else if (keysym == Clutter.w || keysym == Clutter.F4)
else if (keysym === Clutter.KEY_w || keysym === Clutter.KEY_F4)
this._closeAppWindow(this._selectedIndex, this._currentWindow);
else
return Clutter.EVENT_PROPAGATE;
} else if (keysym == Clutter.KEY_Left) {
this._select(this._previous());
} else if (keysym == Clutter.KEY_Right) {
this._select(this._next());
} else if (keysym == Clutter.KEY_Down) {
this._select(this._selectedIndex, 0);
} else {
if (keysym == Clutter.Left)
this._select(this._previous());
else if (keysym == Clutter.Right)
this._select(this._next());
else if (keysym == Clutter.Down)
this._select(this._selectedIndex, 0);
else
return Clutter.EVENT_PROPAGATE;
return Clutter.EVENT_PROPAGATE;
}
return Clutter.EVENT_STOP;
@ -296,9 +293,9 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
/**
* _select:
* @app: index of the app to select
* @window: (optional) index of which of @app's windows to select
* @forceAppFocus: optional flag, see below
* @param {number} app: index of the app to select
* @param {number=} window: index of which of @app's windows to select
* @param {bool} forceAppFocus: optional flag, see below
*
* Selects the indicated @app, and optional @window, and sets
* this._thumbnailsFocused appropriately to indicate whether the
@ -368,15 +365,15 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
onComplete: () => {
thumbnailsActor.destroy();
this.thumbnailsVisible = false;
}
},
});
this._thumbnails = null;
if (this._switcherList._items[this._selectedIndex])
this._switcherList._items[this._selectedIndex].remove_accessible_state (Atk.StateType.EXPANDED);
this._switcherList._items[this._selectedIndex].remove_accessible_state(Atk.StateType.EXPANDED);
}
_createThumbnails() {
this._thumbnails = new ThumbnailList (this._items[this._selectedIndex].cachedWindows);
this._thumbnails = new ThumbnailList(this._items[this._selectedIndex].cachedWindows);
this._thumbnails.connect('item-activated', this._windowActivated.bind(this));
this._thumbnails.connect('item-entered', this._windowEntered.bind(this));
this._thumbnails.connect('item-removed', this._windowRemoved.bind(this));
@ -398,34 +395,33 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this.thumbnailsVisible = true;
}
},
});
this._switcherList._items[this._selectedIndex].add_accessible_state (Atk.StateType.EXPANDED);
this._switcherList._items[this._selectedIndex].add_accessible_state(Atk.StateType.EXPANDED);
}
});
class CyclerHighlight {
constructor() {
var CyclerHighlight = GObject.registerClass(
class CyclerHighlight extends St.Widget {
_init() {
super._init({ layout_manager: new Clutter.BinLayout() });
this._window = null;
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._clone = new Clutter.Clone();
this.actor.add_actor(this._clone);
this.add_actor(this._clone);
this._highlight = new St.Widget({ style_class: 'cycler-highlight' });
this.actor.add_actor(this._highlight);
this.add_actor(this._highlight);
let coordinate = Clutter.BindCoordinate.ALL;
let constraint = new Clutter.BindConstraint({ coordinate: coordinate });
let constraint = new Clutter.BindConstraint({ coordinate });
this._clone.bind_property('source', constraint, 'source', 0);
this.actor.add_constraint(constraint);
this.add_constraint(constraint);
this.actor.connect('notify::allocation',
this._onAllocationChanged.bind(this));
this.actor.connect('destroy', this._onDestroy.bind(this));
this.connect('notify::allocation', this._onAllocationChanged.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
}
set window(w) {
@ -451,7 +447,7 @@ class CyclerHighlight {
this._highlight.set_size(0, 0);
this._highlight.hide();
} else {
let [x, y] = this.actor.allocation.get_origin();
let [x, y] = this.allocation.get_origin();
let rect = this._window.get_frame_rect();
this._highlight.set_size(rect.width, rect.height);
this._highlight.set_position(rect.x - x, rect.y - y);
@ -462,7 +458,7 @@ class CyclerHighlight {
_onDestroy() {
this.window = null;
}
}
});
// We don't show an actual popup, so just provide what SwitcherPopup
// expects instead of inheriting from SwitcherList
@ -478,7 +474,7 @@ var CyclerList = GObject.registerClass({
});
var CyclerPopup = GObject.registerClass({
GTypeFlags: GObject.TypeFlags.ABSTRACT
GTypeFlags: GObject.TypeFlags.ABSTRACT,
}, class CyclerPopup extends SwitcherPopup.SwitcherPopup {
_init() {
super._init();
@ -489,7 +485,7 @@ var CyclerPopup = GObject.registerClass({
return;
this._highlight = new CyclerHighlight();
global.window_group.add_actor(this._highlight.actor);
global.window_group.add_actor(this._highlight);
this._switcherList = new CyclerList();
this._switcherList.connect('item-highlighted', (list, index) => {
@ -499,7 +495,7 @@ var CyclerPopup = GObject.registerClass({
_highlightItem(index, _justOutline) {
this._highlight.window = this._items[index];
global.window_group.set_child_above_sibling(this._highlight.actor, null);
global.window_group.set_child_above_sibling(this._highlight, null);
}
_finish() {
@ -529,7 +525,7 @@ var CyclerPopup = GObject.registerClass({
}
_onDestroy() {
this._highlight.actor.destroy();
this._highlight.destroy();
super._onDestroy();
}
@ -592,20 +588,18 @@ class WindowSwitcherPopup extends SwitcherPopup.SwitcherPopup {
}
_keyPressHandler(keysym, action) {
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) {
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
this._select(this._next());
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD) {
else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD)
this._select(this._previous());
} else {
if (keysym == Clutter.Left)
this._select(this._previous());
else if (keysym == Clutter.Right)
this._select(this._next());
else if (keysym == Clutter.w || keysym == Clutter.F4)
this._closeWindow(this._selectedIndex);
else
return Clutter.EVENT_PROPAGATE;
}
else if (keysym == Clutter.KEY_Left)
this._select(this._previous());
else if (keysym == Clutter.KEY_Right)
this._select(this._next());
else if (keysym == Clutter.KEY_w || keysym == Clutter.KEY_F4)
this._closeWindow(this._selectedIndex);
else
return Clutter.EVENT_PROPAGATE;
return Clutter.EVENT_STOP;
}
@ -648,20 +642,22 @@ class WindowCyclerPopup extends CyclerPopup {
}
});
var AppIcon = GObject.registerClass({
GTypeName: 'AltTab_AppIcon'
}, class AppIcon extends St.BoxLayout {
var AppIcon = GObject.registerClass(
class AppIcon extends St.BoxLayout {
_init(app) {
super._init({ style_class: 'alt-tab-app',
vertical: true });
this.app = app;
this.icon = null;
this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
this._iconBin = new St.Bin();
this.add(this._iconBin, { x_fill: false, y_fill: false } );
this.label = new St.Label({ text: this.app.get_name() });
this.add(this.label, { x_fill: false });
this.add_child(this._iconBin);
this.label = new St.Label({
text: this.app.get_name(),
x_align: Clutter.ActorAlign.CENTER,
});
this.add_child(this.label);
}
// eslint-disable-next-line camelcase
@ -697,7 +693,7 @@ class AppSwitcher extends SwitcherPopup.SwitcherList {
// Cache the window list now; we don't handle dynamic changes here,
// and we don't want to be continually retrieving it
appIcon.cachedWindows = allWindows.filter(
w => windowTracker.get_window_app (w) == appIcon.app
w => windowTracker.get_window_app(w) == appIcon.app
);
if (appIcon.cachedWindows.length > 0)
this._addIcon(appIcon);
@ -721,9 +717,9 @@ class AppSwitcher extends SwitcherPopup.SwitcherList {
_setIconSize() {
let j = 0;
while (this._items.length > 1 && this._items[j].style_class != 'item-box') {
while (this._items.length > 1 && this._items[j].style_class != 'item-box')
j++;
}
let themeNode = this._items[j].get_theme_node();
this._list.ensure_style();
@ -857,7 +853,7 @@ class AppSwitcher extends SwitcherPopup.SwitcherList {
if (appIcon.cachedWindows.length == 1)
arrow.hide();
else
item.add_accessible_state (Atk.StateType.EXPANDABLE);
item.add_accessible_state(Atk.StateType.EXPANDABLE);
}
_removeIcon(app) {
@ -893,12 +889,13 @@ class ThumbnailList extends SwitcherPopup.SwitcherList {
let title = windows[i].get_title();
if (title) {
let name = new St.Label({ text: title });
// St.Label doesn't support text-align so use a Bin
let bin = new St.Bin({ x_align: St.Align.MIDDLE });
this._labels.push(bin);
bin.add_actor(name);
box.add_actor(bin);
let name = new St.Label({
text: title,
// St.Label doesn't support text-align
x_align: Clutter.ActorAlign.CENTER,
});
this._labels.push(name);
box.add_actor(name);
this.addItem(box, name);
} else {
@ -977,7 +974,7 @@ class WindowIcon extends St.BoxLayout {
this._icon = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this.add(this._icon, { x_fill: false, y_fill: false } );
this.add_child(this._icon);
this.label = new St.Label({ text: window.get_title() });
let tracker = Shell.WindowTracker.get_default();
@ -1000,9 +997,10 @@ class WindowIcon extends St.BoxLayout {
size = WINDOW_PREVIEW_SIZE;
this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor));
if (this.app)
if (this.app) {
this._icon.add_actor(this._createAppIcon(this.app,
APP_ICON_SIZE_SMALL));
}
break;
case AppIconMode.APP_ICON_ONLY:

View File

@ -1,19 +1,20 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Animation, AnimatedIcon, Spinner */
const { Clutter, GLib, Gio, St } = imports.gi;
const { Clutter, GLib, GObject, Gio, St } = imports.gi;
const Params = imports.misc.params;
var ANIMATED_ICON_UPDATE_TIMEOUT = 16;
var SPINNER_ANIMATION_TIME = 300;
var SPINNER_ANIMATION_DELAY = 1000;
var Animation = class {
constructor(file, width, height, speed) {
this.actor = new St.Bin();
this.actor.set_size(width, height);
this.actor.connect('destroy', this._onDestroy.bind(this));
this.actor.connect('notify::size', this._syncAnimationSize.bind(this));
this.actor.connect('resource-scale-changed',
var Animation = GObject.registerClass(
class Animation extends St.Bin {
_init(file, width, height, speed) {
super._init({ width, height });
this.connect('destroy', this._onDestroy.bind(this));
this.connect('resource-scale-changed',
this._loadFile.bind(this, file, width, height));
let themeContext = St.ThemeContext.get_for_stage(global.stage);
@ -52,14 +53,14 @@ var Animation = class {
}
_loadFile(file, width, height) {
let [validResourceScale, resourceScale] = this.actor.get_resource_scale();
let [validResourceScale, resourceScale] = this.get_resource_scale();
let wasPlaying = this._isPlaying;
if (this._isPlaying)
this.stop();
this._isLoaded = false;
this.actor.destroy_all_children();
this.destroy_all_children();
if (!validResourceScale) {
if (wasPlaying)
@ -72,7 +73,11 @@ var Animation = class {
this._animations = textureCache.load_sliced_image(file, width, height,
scaleFactor, resourceScale,
this._animationsLoaded.bind(this));
this.actor.set_child(this._animations);
this._animations.set({
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
});
this.set_child(this._animations);
if (wasPlaying)
this.play();
@ -83,7 +88,7 @@ var Animation = class {
if (oldFrameActor)
oldFrameActor.hide();
this._frame = (frame % this._animations.get_n_children());
this._frame = frame % this._animations.get_n_children();
let newFrameActor = this._animations.get_child_at_index(this._frame);
if (newFrameActor)
@ -99,7 +104,7 @@ var Animation = class {
if (!this._isLoaded)
return;
let [width, height] = this.actor.get_size();
let [width, height] = this.get_size();
for (let i = 0; i < this._animations.get_n_children(); ++i)
this._animations.get_child_at_index(i).set_size(width, height);
@ -122,21 +127,29 @@ var Animation = class {
themeContext.disconnect(this._scaleChangedId);
this._scaleChangedId = 0;
}
};
});
var AnimatedIcon = class extends Animation {
constructor(file, size) {
super(file, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
var AnimatedIcon = GObject.registerClass(
class AnimatedIcon extends Animation {
_init(file, size) {
super._init(file, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
}
};
});
var Spinner = class extends AnimatedIcon {
constructor(size, animate = false) {
var Spinner = GObject.registerClass(
class Spinner extends AnimatedIcon {
_init(size, params) {
params = Params.parse(params, {
animate: false,
hideOnStop: false,
});
let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
super(file, size);
super._init(file, size);
this.actor.opacity = 0;
this._animate = animate;
this.opacity = 0;
this._animate = params.animate;
this._hideOnStop = params.hideOnStop;
this.visible = !this._hideOnStop;
}
_onDestroy() {
@ -145,35 +158,43 @@ var Spinner = class extends AnimatedIcon {
}
play() {
this.actor.remove_all_transitions();
this.remove_all_transitions();
this.show();
if (this._animate) {
super.play();
this.actor.ease({
this.ease({
opacity: 255,
delay: SPINNER_ANIMATION_DELAY,
duration: SPINNER_ANIMATION_TIME,
mode: Clutter.AnimationMode.LINEAR
mode: Clutter.AnimationMode.LINEAR,
});
} else {
this.actor.opacity = 255;
this.opacity = 255;
super.play();
}
}
stop() {
this.actor.remove_all_transitions();
this.remove_all_transitions();
if (this._animate) {
this.actor.ease({
this.ease({
opacity: 0,
duration: SPINNER_ANIMATION_TIME,
mode: Clutter.AnimationMode.LINEAR,
onComplete: () => super.stop()
onComplete: () => {
super.stop();
if (this._hideOnStop)
this.hide();
},
});
} else {
this.actor.opacity = 0;
this.opacity = 0;
super.stop();
if (this._hideOnStop)
this.hide();
}
}
};
});

File diff suppressed because it is too large Load Diff

View File

@ -55,6 +55,7 @@ const RENAMED_DESKTOP_IDS = {
'org.gnome.taquin.desktop': 'org.gnome.Taquin.desktop',
'org.gnome.Weather.Application.desktop': 'org.gnome.Weather.desktop',
'polari.desktop': 'org.gnome.Polari.desktop',
'shotwell.desktop': 'org.gnome.Shotwell.desktop',
'tali.desktop': 'org.gnome.Tali.desktop',
'totem.desktop': 'org.gnome.Totem.desktop',
'evince.desktop': 'org.gnome.Evince.desktop',

View File

@ -9,13 +9,13 @@ const { loadInterfaceXML } = imports.misc.fileUtils;
var AudioDevice = {
HEADPHONES: 1 << 0,
HEADSET: 1 << 1,
MICROPHONE: 1 << 2
MICROPHONE: 1 << 2,
};
const AudioDeviceSelectionIface = loadInterfaceXML('org.gnome.Shell.AudioDeviceSelection');
var AudioDeviceSelectionDialog = GObject.registerClass({
Signals: { 'device-selected': { param_types: [GObject.TYPE_UINT] } }
Signals: { 'device-selected': { param_types: [GObject.TYPE_UINT] } },
}, class AudioDeviceSelectionDialog extends ModalDialog.ModalDialog {
_init(devices) {
super._init({ styleClass: 'audio-device-selection-dialog' });
@ -43,15 +43,19 @@ var AudioDeviceSelectionDialog = GObject.registerClass({
this.contentLayout.style_class = 'audio-selection-content';
this.contentLayout.add(title);
this._selectionBox = new St.BoxLayout({ style_class: 'audio-selection-box' });
this.contentLayout.add(this._selectionBox, { expand: true });
this._selectionBox = new St.BoxLayout({
style_class: 'audio-selection-box',
x_expand: true,
});
this.contentLayout.add_child(this._selectionBox);
if (Main.sessionMode.allowSettings)
if (Main.sessionMode.allowSettings) {
this.addButton({ action: this._openSettings.bind(this),
label: _("Sound Settings") });
}
this.addButton({ action: this.close.bind(this),
label: _("Cancel"),
key: Clutter.Escape });
key: Clutter.KEY_Escape });
}
_getDeviceLabel(device) {

View File

@ -1,4 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported SystemBackground */
// READ THIS FIRST
// Background handling is a maze of objects, both objects in this file, and
@ -93,7 +94,7 @@
// MetaBackgroundImage MetaBackgroundImage
// MetaBackgroundImage MetaBackgroundImage
const { Clutter, GDesktopEnums, Gio, GLib, GnomeDesktop, Meta } = imports.gi;
const { Clutter, GDesktopEnums, Gio, GLib, GObject, GnomeDesktop, Meta } = imports.gi;
const Signals = imports.signals;
const LoginManager = imports.misc.loginManager;
@ -144,7 +145,7 @@ var BackgroundCache = class BackgroundCache {
let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
monitor.connect('changed',
(obj, file, otherFile, eventType) => {
(obj, theFile, otherFile, eventType) => {
// Ignore CHANGED and CREATED events, since in both cases
// we'll get a CHANGES_DONE_HINT event when done.
if (eventType != Gio.FileMonitorEvent.CHANGED &&
@ -220,16 +221,17 @@ function getBackgroundCache() {
return _backgroundCache;
}
var Background = class Background {
constructor(params) {
var Background = GObject.registerClass({
Signals: { 'loaded': {}, 'bg-changed': {} },
}, class Background extends Meta.Background {
_init(params) {
params = Params.parse(params, { monitorIndex: 0,
layoutManager: Main.layoutManager,
settings: null,
file: null,
style: null });
this.background = new Meta.Background({ meta_display: global.display });
this.background._delegate = this;
super._init({ meta_display: global.display });
this._settings = params.settings;
this._file = params.file;
@ -262,16 +264,14 @@ var Background = class Background {
}
destroy() {
this.background = null;
this._cancellable.cancel();
this._removeAnimationTimeout();
let i;
let keys = Object.keys(this._fileWatches);
for (i = 0; i < keys.length; i++) {
for (i = 0; i < keys.length; i++)
this._cache.disconnect(this._fileWatches[keys[i]]);
}
this._fileWatches = null;
if (this._timezoneChangedId != 0)
@ -300,9 +300,11 @@ var Background = class Background {
this._changedIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this._changedIdleId = 0;
this.emit('changed');
this.emit('bg-changed');
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(this._changedIdleId,
'[gnome-shell] Background._emitChangedSignal');
}
updateResolution() {
@ -328,7 +330,7 @@ var Background = class Background {
this.emit('loaded');
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(id, '[gnome-shell] this.emit');
GLib.Source.set_name_by_id(id, '[gnome-shell] Background._setLoaded Idle');
}
_loadPattern() {
@ -342,9 +344,9 @@ var Background = class Background {
let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
if (shadingType == GDesktopEnums.BackgroundShading.SOLID)
this.background.set_color(color);
this.set_color(color);
else
this.background.set_gradient(shadingType, color, secondColor);
this.set_gradient(shadingType, color, secondColor);
}
_watchFile(file) {
@ -380,13 +382,13 @@ var Background = class Background {
let finish = () => {
this._setLoaded();
if (files.length > 1) {
this.background.set_blend(files[0], files[1],
this._animation.transitionProgress,
this._style);
this.set_blend(files[0], files[1],
this._animation.transitionProgress,
this._style);
} else if (files.length > 0) {
this.background.set_file(files[0], this._style);
this.set_file(files[0], this._style);
} else {
this.background.set_file(null, this._style);
this.set_file(null, this._style);
}
this._queueUpdateAnimation();
};
@ -401,6 +403,7 @@ var Background = class Background {
if (numPendingImages == 0)
finish();
} else {
// eslint-disable-next-line no-loop-func
let id = image.connect('loaded', () => {
image.disconnect(id);
numPendingImages--;
@ -442,7 +445,7 @@ var Background = class Background {
_loadAnimation(file) {
this._cache.getAnimation({
file: file,
file,
settingsSchema: this._settings.schema_id,
onLoaded: animation => {
this._animation = animation;
@ -454,12 +457,12 @@ var Background = class Background {
this._updateAnimation();
this._watchFile(file);
}
},
});
}
_loadImage(file) {
this.background.set_file(file, this._style);
this.set_file(file, this._style);
this._watchFile(file);
let cache = Meta.BackgroundImageCache.get_default();
@ -493,13 +496,14 @@ var Background = class Background {
this._loadFile(this._file);
}
};
Signals.addSignalMethods(Background.prototype);
});
let _systemBackground;
var SystemBackground = class SystemBackground {
constructor() {
var SystemBackground = GObject.registerClass({
Signals: { 'loaded': {} },
}, class SystemBackground extends Meta.BackgroundActor {
_init() {
let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/noise-texture.png');
if (_systemBackground == null) {
@ -508,9 +512,11 @@ var SystemBackground = class SystemBackground {
_systemBackground.set_file(file, GDesktopEnums.BackgroundStyle.WALLPAPER);
}
this.actor = new Meta.BackgroundActor({ meta_display: global.display,
monitor: 0,
background: _systemBackground });
super._init({
meta_display: global.display,
monitor: 0,
background: _systemBackground,
});
let cache = Meta.BackgroundImageCache.get_default();
let image = cache.load(file);
@ -529,8 +535,7 @@ var SystemBackground = class SystemBackground {
});
}
}
};
Signals.addSignalMethods(SystemBackground.prototype);
});
var BackgroundSource = class BackgroundSource {
constructor(layoutManager, settingsSchema) {
@ -566,7 +571,7 @@ var BackgroundSource = class BackgroundSource {
// We don't watch changes to settings here,
// instead we rely on Background to watch those
// and emit 'changed' at the right time
// and emit 'bg-changed' at the right time
if (this._overrideImage != null) {
file = Gio.File.new_for_path(this._overrideImage);
@ -588,14 +593,14 @@ var BackgroundSource = class BackgroundSource {
if (!(monitorIndex in this._backgrounds)) {
let background = new Background({
monitorIndex: monitorIndex,
monitorIndex,
layoutManager: this._layoutManager,
settings: this._settings,
file: file,
style: style
file,
style,
});
background._changedId = background.connect('changed', () => {
background._changedId = background.connect('bg-changed', () => {
background.disconnect(background._changedId);
background.destroy();
delete this._backgrounds[monitorIndex];
@ -621,11 +626,11 @@ var BackgroundSource = class BackgroundSource {
}
};
var Animation = class Animation {
constructor(params) {
params = Params.parse(params, { file: null });
var Animation = GObject.registerClass(
class Animation extends GnomeDesktop.BGSlideShow {
_init(params) {
super._init(params);
this.file = params.file;
this.keyFrameFiles = [];
this.transitionProgress = 0.0;
this.transitionDuration = 0.0;
@ -633,9 +638,7 @@ var Animation = class Animation {
}
load(callback) {
this._show = new GnomeDesktop.BGSlideShow({ file: this.file });
this._show.load_async(null, () => {
this.load_async(null, () => {
this.loaded = true;
if (callback)
callback();
@ -645,13 +648,11 @@ var Animation = class Animation {
update(monitor) {
this.keyFrameFiles = [];
if (!this._show)
if (this.get_num_slides() < 1)
return;
if (this._show.get_num_slides() < 1)
return;
let [progress, duration, isFixed_, filename1, filename2] = this._show.get_current_slide(monitor.width, monitor.height);
let [progress, duration, isFixed_, filename1, filename2] =
this.get_current_slide(monitor.width, monitor.height);
this.transitionDuration = duration;
this.transitionProgress = progress;
@ -662,8 +663,7 @@ var Animation = class Animation {
if (filename2)
this.keyFrameFiles.push(Gio.File.new_for_path(filename2));
}
};
Signals.addSignalMethods(Animation.prototype);
});
var BackgroundManager = class BackgroundManager {
constructor(params) {
@ -714,7 +714,7 @@ var BackgroundManager = class BackgroundManager {
opacity: 0,
duration: FADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => oldBackgroundActor.destroy()
onComplete: () => oldBackgroundActor.destroy(),
});
}
@ -732,7 +732,7 @@ var BackgroundManager = class BackgroundManager {
this._newBackgroundActor = newBackgroundActor;
let background = newBackgroundActor.background._delegate;
let background = newBackgroundActor.background;
if (background.isLoaded) {
this._swapBackgroundActor();
@ -752,7 +752,7 @@ var BackgroundManager = class BackgroundManager {
let backgroundActor = new Meta.BackgroundActor({
meta_display: global.display,
monitor: this._monitorIndex,
background: background.background,
background,
vignette: this._vignette,
vignette_sharpness: 0.5,
brightness: 0.5,
@ -763,10 +763,10 @@ var BackgroundManager = class BackgroundManager {
if (this._controlPosition) {
let monitor = this._layoutManager.monitors[this._monitorIndex];
backgroundActor.set_position(monitor.x, monitor.y);
backgroundActor.lower_bottom();
this._container.set_child_below_sibling(backgroundActor, null);
}
let changeSignalId = background.connect('changed', () => {
let changeSignalId = background.connect('bg-changed', () => {
background.disconnect(changeSignalId);
changeSignalId = null;
this._updateBackgroundActor();

View File

@ -35,11 +35,12 @@ function addBackgroundMenu(actor, layoutManager) {
}
let clickAction = new Clutter.ClickAction();
clickAction.connect('long-press', (action, actor, state) => {
if (state == Clutter.LongPressState.QUERY)
return ((action.get_button() == 0 ||
clickAction.connect('long-press', (action, theActor, state) => {
if (state == Clutter.LongPressState.QUERY) {
return (action.get_button() == 0 ||
action.get_button() == 1) &&
!actor._backgroundMenu.isOpen);
!actor._backgroundMenu.isOpen;
}
if (state == Clutter.LongPressState.ACTIVATE) {
let [x, y] = action.get_coords();
openMenu(x, y);

View File

@ -16,8 +16,8 @@ var BarLevel = GObject.registerClass({
'overdrive-start': GObject.ParamSpec.double(
'overdrive-start', 'overdrive-start', 'overdrive-start',
GObject.ParamFlags.READWRITE,
1, 2, 1)
}
1, 2, 1),
},
}, class BarLevel extends St.DrawingArea {
_init(params) {
this._maxValue = 1;
@ -27,7 +27,7 @@ var BarLevel = GObject.registerClass({
let defaultParams = {
style_class: 'barlevel',
accessible_role: Atk.Role.LEVEL_BAR
accessible_role: Atk.Role.LEVEL_BAR,
};
super._init(Object.assign(defaultParams, params));
this.connect('allocation-changed', (actor, box) => {
@ -88,9 +88,10 @@ var BarLevel = GObject.registerClass({
if (this._overdriveStart == value)
return;
if (value > this._maxValue)
if (value > this._maxValue) {
throw new Error(`Tried to set overdrive value to ${value}, ` +
`which is a number greater than the maximum allowed value ${this._maxValue}`);
}
this._overdriveStart = value;
this.notify('overdrive-start');

View File

@ -44,14 +44,20 @@ var BoxPointer = GObject.registerClass({
this._border = new St.DrawingArea();
this._border.connect('repaint', this._drawBorder.bind(this));
this.add_actor(this._border);
this.bin.raise(this._border);
this.set_child_above_sibling(this.bin, this._border);
this._sourceAlignment = 0.5;
this._capturedEventId = 0;
this._muteInput();
this._muteInput = true;
this.connect('destroy', this._onDestroy.bind(this));
}
vfunc_captured_event() {
if (this._muteInput)
return Clutter.EVENT_STOP;
return Clutter.EVENT_PROPAGATE;
}
_onDestroy() {
if (this._sourceActorDestroyId) {
this._sourceActor.disconnect(this._sourceActorDestroyId);
@ -63,23 +69,10 @@ var BoxPointer = GObject.registerClass({
return this._arrowSide;
}
_muteInput() {
if (this._capturedEventId == 0)
this._capturedEventId = this.connect('captured-event',
() => Clutter.EVENT_STOP);
}
_unmuteInput() {
if (this._capturedEventId != 0) {
this.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
}
open(animate, onComplete) {
let themeNode = this.get_theme_node();
let rise = themeNode.get_length('-arrow-rise');
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
let animationTime = animate & PopupAnimation.FULL ? POPUP_ANIMATION_TIME : 0;
if (animate & PopupAnimation.FADE)
this.opacity = 0;
@ -112,10 +105,10 @@ var BoxPointer = GObject.registerClass({
duration: animationTime,
mode: Clutter.AnimationMode.LINEAR,
onComplete: () => {
this._unmuteInput();
this._muteInput = false;
if (onComplete)
onComplete();
}
},
});
}
@ -127,8 +120,8 @@ var BoxPointer = GObject.registerClass({
let translationY = 0;
let themeNode = this.get_theme_node();
let rise = themeNode.get_length('-arrow-rise');
let fade = (animate & PopupAnimation.FADE);
let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0;
let fade = animate & PopupAnimation.FADE;
let animationTime = animate & PopupAnimation.FULL ? POPUP_ANIMATION_TIME : 0;
if (animate & PopupAnimation.SLIDE) {
switch (this._arrowSide) {
@ -147,7 +140,7 @@ var BoxPointer = GObject.registerClass({
}
}
this._muteInput();
this._muteInput = true;
this.remove_all_transitions();
this.ease({
@ -163,7 +156,7 @@ var BoxPointer = GObject.registerClass({
this.translation_y = 0;
if (onComplete)
onComplete();
}
},
});
}
@ -254,11 +247,10 @@ var BoxPointer = GObject.registerClass({
let [absX, absY] = this.get_transformed_position();
if (this._arrowSide == St.Side.TOP ||
this._arrowSide == St.Side.BOTTOM) {
this._arrowSide == St.Side.BOTTOM)
this._arrowOrigin = sourceX - absX + sourceWidth / 2;
} else {
else
this._arrowOrigin = sourceY - absY + sourceHeight / 2;
}
}
let borderWidth = themeNode.get_length('-arrow-border-width');
@ -273,20 +265,19 @@ var BoxPointer = GObject.registerClass({
let [width, height] = area.get_surface_size();
let [boxWidth, boxHeight] = [width, height];
if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM) {
if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM)
boxHeight -= rise;
} else {
else
boxWidth -= rise;
}
let cr = area.get_context();
// Translate so that box goes from 0,0 to boxWidth,boxHeight,
// with the arrow poking out of that
if (this._arrowSide == St.Side.TOP) {
if (this._arrowSide == St.Side.TOP)
cr.translate(0, rise);
} else if (this._arrowSide == St.Side.LEFT) {
else if (this._arrowSide == St.Side.LEFT)
cr.translate(rise, 0);
}
let [x1, y1] = [halfBorder, halfBorder];
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
@ -482,7 +473,7 @@ var BoxPointer = GObject.registerClass({
let borderWidth = themeNode.get_length('-arrow-border-width');
let arrowBase = themeNode.get_length('-arrow-base');
let borderRadius = themeNode.get_length('-arrow-border-radius');
let margin = (4 * borderRadius + borderWidth + arrowBase);
let margin = 4 * borderRadius + borderWidth + arrowBase;
let gap = themeNode.get_length('-boxpointer-gap');
let padding = themeNode.get_length('-arrow-rise');
@ -533,11 +524,11 @@ var BoxPointer = GObject.registerClass({
arrowOrigin = sourceCenterX - resX;
if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
if (arrowOrigin > x1)
resX += (arrowOrigin - x1);
resX += arrowOrigin - x1;
arrowOrigin = x1;
} else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
if (arrowOrigin < x2)
resX -= (x2 - arrowOrigin);
resX -= x2 - arrowOrigin;
arrowOrigin = x2;
}
break;
@ -552,11 +543,11 @@ var BoxPointer = GObject.registerClass({
arrowOrigin = sourceCenterY - resY;
if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
if (arrowOrigin > y1)
resY += (arrowOrigin - y1);
resY += arrowOrigin - y1;
arrowOrigin = y1;
} else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
if (arrowOrigin < y2)
resX -= (y2 - arrowOrigin);
resX -= y2 - arrowOrigin;
arrowOrigin = y2;
}
break;

View File

@ -1,8 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Calendar, CalendarMessageList */
/* exported Calendar, CalendarMessageList, DBusEventSource */
const { Clutter, Gio, GLib, GObject, Shell, St } = imports.gi;
const Signals = imports.signals;
const Main = imports.ui.main;
const MessageList = imports.ui.messageList;
@ -21,7 +20,7 @@ var MESSAGE_ICON_SIZE = -1; // pick up from CSS
var NC_ = (context, str) => `${context}\u0004${str}`;
function sameYear(dateA, dateB) {
return (dateA.getYear() == dateB.getYear());
return dateA.getYear() == dateB.getYear();
}
function sameMonth(dateA, dateB) {
@ -79,7 +78,7 @@ function _getCalendarDayAbbreviation(dayNumber) {
/* Translators: Calendar grid abbreviation for Friday */
NC_("grid friday", "F"),
/* Translators: Calendar grid abbreviation for Saturday */
NC_("grid saturday", "S")
NC_("grid saturday", "S"),
];
return Shell.util_translate_time_string(abbreviations[dayNumber]);
}
@ -99,17 +98,54 @@ var CalendarEvent = class CalendarEvent {
// Interface for appointments/events - e.g. the contents of a calendar
//
// First, an implementation with no events
var EmptyEventSource = class EmptyEventSource {
constructor() {
this.isLoading = false;
this.isDummy = true;
this.hasCalendars = false;
var EventSourceBase = GObject.registerClass({
GTypeFlags: GObject.TypeFlags.ABSTRACT,
Properties: {
'has-calendars': GObject.ParamSpec.boolean(
'has-calendars', 'has-calendars', 'has-calendars',
GObject.ParamFlags.READABLE,
false),
'is-loading': GObject.ParamSpec.boolean(
'is-loading', 'is-loading', 'is-loading',
GObject.ParamFlags.READABLE,
false),
},
Signals: { 'changed': {} },
}, class EventSourceBase extends GObject.Object {
get isLoading() {
throw new GObject.NotImplementedError(`isLoading in ${this.constructor.name}`);
}
get hasCalendars() {
throw new GObject.NotImplementedError(`hasCalendars in ${this.constructor.name}`);
}
destroy() {
}
requestRange(_begin, _end) {
throw new GObject.NotImplementedError(`requestRange in ${this.constructor.name}`);
}
getEvents(_begin, _end) {
throw new GObject.NotImplementedError(`getEvents in ${this.constructor.name}`);
}
hasEvents(_day) {
throw new GObject.NotImplementedError(`hasEvents in ${this.constructor.name}`);
}
});
var EmptyEventSource = GObject.registerClass(
class EmptyEventSource extends EventSourceBase {
get isLoading() {
return false;
}
get hasCalendars() {
return false;
}
requestRange(_begin, _end) {
}
@ -121,8 +157,7 @@ var EmptyEventSource = class EmptyEventSource {
hasEvents(_day) {
return false;
}
};
Signals.addSignalMethods(EmptyEventSource.prototype);
});
const CalendarServerIface = loadInterfaceXML('org.gnome.Shell.CalendarServer');
@ -154,11 +189,12 @@ function _dateIntervalsOverlap(a0, a1, b0, b1) {
}
// an implementation that reads data from a session bus service
var DBusEventSource = class DBusEventSource {
constructor() {
var DBusEventSource = GObject.registerClass(
class DBusEventSource extends EventSourceBase {
_init() {
super._init();
this._resetCache();
this.isLoading = false;
this.isDummy = false;
this._isLoading = false;
this._initialized = false;
this._dbusProxy = new CalendarServer();
@ -193,12 +229,12 @@ var DBusEventSource = class DBusEventSource {
});
this._dbusProxy.connect('g-properties-changed', () => {
this.emit('notify::has-calendars');
this.notify('has-calendars');
});
this._initialized = loaded;
if (loaded) {
this.emit('notify::has-calendars');
this.notify('has-calendars');
this._onNameAppeared();
}
});
@ -215,6 +251,10 @@ var DBusEventSource = class DBusEventSource {
return false;
}
get isLoading() {
return this._isLoading;
}
_resetCache() {
this._events = [];
this._lastRequestBegin = null;
@ -252,7 +292,7 @@ var DBusEventSource = class DBusEventSource {
newEvents.sort((ev1, ev2) => ev1.date.getTime() - ev2.date.getTime());
this._events = newEvents;
this.isLoading = false;
this._isLoading = false;
this.emit('changed');
}
@ -272,7 +312,7 @@ var DBusEventSource = class DBusEventSource {
requestRange(begin, end) {
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this.isLoading = true;
this._isLoading = true;
this._lastRequestBegin = begin;
this._lastRequestEnd = end;
this._curRequestBegin = begin;
@ -286,9 +326,8 @@ var DBusEventSource = class DBusEventSource {
for (let n = 0; n < this._events.length; n++) {
let event = this._events[n];
if (_dateIntervalsOverlap (event.date, event.end, begin, end)) {
if (_dateIntervalsOverlap(event.date, event.end, begin, end))
result.push(event);
}
}
result.sort((event1, event2) => {
// sort events by end time on ending day
@ -310,11 +349,12 @@ var DBusEventSource = class DBusEventSource {
return true;
}
};
Signals.addSignalMethods(DBusEventSource.prototype);
});
var Calendar = class Calendar {
constructor() {
var Calendar = GObject.registerClass({
Signals: { 'selected-date-changed': { param_types: [GLib.DateTime.$gtype] } },
}, class Calendar extends St.Widget {
_init() {
this._weekStart = Shell.util_get_week_start();
this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.calendar' });
@ -344,19 +384,19 @@ var Calendar = class Calendar {
this._shouldDateGrabFocus = false;
this.actor = new St.Widget({ style_class: 'calendar',
layout_manager: new Clutter.TableLayout(),
reactive: true });
super._init({
style_class: 'calendar',
layout_manager: new Clutter.GridLayout(),
reactive: true,
});
this.actor.connect('scroll-event',
this._onScroll.bind(this));
this._buildHeader ();
this._buildHeader();
}
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
setEventSource(eventSource) {
if (!(eventSource instanceof EventSourceBase))
throw new Error('Event source is not valid type');
this._eventSource = eventSource;
this._eventSource.connect('changed', () => {
this._rebuildCalendar();
@ -373,7 +413,10 @@ var Calendar = class Calendar {
this._selectedDate = date;
this._update();
this.emit('selected-date-changed', new Date(this._selectedDate));
let datetime = GLib.DateTime.new_from_unix_local(
this._selectedDate.getTime() / 1000);
this.emit('selected-date-changed', datetime);
}
updateTimeZone() {
@ -384,14 +427,13 @@ var Calendar = class Calendar {
}
_buildHeader() {
let layout = this.actor.layout_manager;
let layout = this.layout_manager;
let offsetCols = this._useWeekdate ? 1 : 0;
this.actor.destroy_all_children();
this.destroy_all_children();
// Top line of the calendar '<| September 2009 |>'
this._topBox = new St.BoxLayout();
layout.pack(this._topBox, 0, 0);
layout.set_span(this._topBox, offsetCols + 7, 1);
layout.attach(this._topBox, 0, 0, offsetCols + 7, 1);
this._backButton = new St.Button({ style_class: 'calendar-change-month-back pager-button',
accessible_name: _("Previous month"),
@ -400,9 +442,13 @@ var Calendar = class Calendar {
this._topBox.add(this._backButton);
this._backButton.connect('clicked', this._onPrevMonthButtonClicked.bind(this));
this._monthLabel = new St.Label({ style_class: 'calendar-month-label',
can_focus: true });
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
this._monthLabel = new St.Label({
style_class: 'calendar-month-label',
can_focus: true,
x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
});
this._topBox.add_child(this._monthLabel);
this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward pager-button',
accessible_name: _("Next month"),
@ -428,20 +474,20 @@ var Calendar = class Calendar {
can_focus: true });
label.accessible_name = iter.toLocaleFormat('%A');
let col;
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
if (this.get_text_direction() == Clutter.TextDirection.RTL)
col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
else
col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
layout.pack(label, col, 1);
layout.attach(label, col, 1, 1, 1);
iter.setTime(iter.getTime() + MSECS_IN_DAY);
}
// All the children after this are days, and get removed when we update the calendar
this._firstDayIndex = this.actor.get_n_children();
this._firstDayIndex = this.get_n_children();
}
_onScroll(actor, event) {
switch (event.get_scroll_direction()) {
vfunc_scroll_event(scrollEvent) {
switch (scrollEvent.direction) {
case Clutter.ScrollDirection.UP:
case Clutter.ScrollDirection.LEFT:
this._onPrevMonthButtonClicked();
@ -511,7 +557,7 @@ var Calendar = class Calendar {
let now = new Date();
// Remove everything but the topBox and the weekday labels
let children = this.actor.get_children();
let children = this.get_children();
for (let i = this._firstDayIndex; i < children.length; i++)
children[i].destroy();
@ -548,7 +594,7 @@ var Calendar = class Calendar {
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
let layout = this.actor.layout_manager;
let layout = this.layout_manager;
let iter = new Date(beginDate);
let row = 2;
// nRows here means 6 weeks + one header + one navbar
@ -559,7 +605,7 @@ var Calendar = class Calendar {
can_focus: true });
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
if (this._eventSource.isDummy)
if (this._eventSource instanceof EmptyEventSource)
button.reactive = false;
button._date = new Date(iter);
@ -603,7 +649,7 @@ var Calendar = class Calendar {
col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
else
col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
layout.pack(button, col, row);
layout.attach(button, col, row, 1, 1);
this._buttons.push(button);
@ -613,7 +659,7 @@ var Calendar = class Calendar {
can_focus: true });
let weekFormat = Shell.util_translate_time_string(N_("Week %V"));
label.accessible_name = iter.toLocaleFormat(weekFormat);
layout.pack(label, rtl ? 7 : 0, row);
layout.attach(label, rtl ? 7 : 0, row, 1, 1);
}
iter.setTime(iter.getTime() + MSECS_IN_DAY);
@ -648,12 +694,12 @@ var Calendar = class Calendar {
}
});
}
};
Signals.addSignalMethods(Calendar.prototype);
});
var EventMessage = class EventMessage extends MessageList.Message {
constructor(event, date) {
super('', event.summary);
var EventMessage = GObject.registerClass(
class EventMessage extends MessageList.Message {
_init(event, date) {
super._init('', event.summary);
this._event = event;
this._date = date;
@ -662,18 +708,19 @@ var EventMessage = class EventMessage extends MessageList.Message {
this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' });
this.setIcon(this._icon);
}
this.actor.connect('style-changed', () => {
let iconVisible = this.actor.get_parent().has_style_pseudo_class('first-child');
this._icon.opacity = (iconVisible ? 255 : 0);
});
vfunc_style_changed() {
let iconVisible = this.get_parent().has_style_pseudo_class('first-child');
this._icon.opacity = iconVisible ? 255 : 0;
super.vfunc_style_changed();
}
_formatEventTime() {
let periodBegin = _getBeginningOfDay(this._date);
let periodEnd = _getEndOfDay(this._date);
let allDay = (this._event.allDay || (this._event.date <= periodBegin &&
this._event.end >= periodEnd));
let allDay = this._event.allDay || (this._event.date <= periodBegin &&
this._event.end >= periodEnd);
let title;
if (allDay) {
/* Translators: Shown in calendar event list for all day events
@ -702,12 +749,12 @@ var EventMessage = class EventMessage extends MessageList.Message {
}
return title;
}
};
});
var NotificationMessage =
var NotificationMessage = GObject.registerClass(
class NotificationMessage extends MessageList.Message {
constructor(notification) {
super(notification.title, notification.bannerBodyText);
_init(notification) {
super._init(notification.title, notification.bannerBodyText);
this.setUseBodyMarkup(notification.bannerBodyMarkup);
this.notification = notification;
@ -730,11 +777,12 @@ class NotificationMessage extends MessageList.Message {
}
_getIcon() {
if (this.notification.gicon)
if (this.notification.gicon) {
return new St.Icon({ gicon: this.notification.gicon,
icon_size: MESSAGE_ICON_SIZE });
else
} else {
return this.notification.source.createIcon(MESSAGE_ICON_SIZE);
}
}
_onUpdated(n, _clear) {
@ -744,7 +792,7 @@ class NotificationMessage extends MessageList.Message {
this.setUseBodyMarkup(n.bannerBodyMarkup);
}
_onClicked() {
vfunc_clicked() {
this.notification.activate();
}
@ -766,11 +814,12 @@ class NotificationMessage extends MessageList.Message {
canClose() {
return true;
}
};
});
var EventsSection = class EventsSection extends MessageList.MessageListSection {
constructor() {
super();
var EventsSection = GObject.registerClass(
class EventsSection extends MessageList.MessageListSection {
_init() {
super._init();
this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', this._reloadEvents.bind(this));
@ -780,9 +829,9 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
this._title = new St.Button({ style_class: 'events-section-title',
label: '',
x_align: St.Align.START,
can_focus: true });
this.actor.insert_child_below(this._title, null);
this._title.child.x_align = Clutter.ActorAlign.START;
this.insert_child_below(this._title, null);
this._title.connect('clicked', this._onTitleClicked.bind(this));
this._title.connect('key-focus-in', this._onKeyFocusIn.bind(this));
@ -793,6 +842,9 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
}
setEventSource(eventSource) {
if (!(eventSource instanceof EventSourceBase))
throw new Error('Event source is not valid type');
this._eventSource = eventSource;
this._eventSource.connect('changed', this._reloadEvents.bind(this));
}
@ -809,14 +861,15 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
let dayFormat;
let now = new Date();
if (sameYear(this._date, now))
if (sameYear(this._date, now)) {
/* Translators: Shown on calendar heading when selected day occurs on current year */
dayFormat = Shell.util_translate_time_string(NC_("calendar heading",
"%A, %B %-d"));
else
} else {
/* Translators: Shown on calendar heading when selected day occurs on different year */
dayFormat = Shell.util_translate_time_string(NC_("calendar heading",
"%A, %B %-d, %Y"));
}
this._title.label = this._date.toLocaleFormat(dayFormat);
}
@ -857,7 +910,7 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
_appInstalledChanged() {
this._calendarApp = undefined;
this._title.reactive = (this._getCalendarApp() != null);
this._title.reactive = this._getCalendarApp() != null;
}
_getCalendarApp() {
@ -901,12 +954,29 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
super._sync();
}
};
});
var NotificationSection =
var TimeLabel = GObject.registerClass(
class NotificationTimeLabel extends St.Label {
_init(datetime) {
super._init({
style_class: 'event-time',
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.END,
});
this._datetime = datetime;
}
vfunc_map() {
this.text = Util.formatTimeSpan(this._datetime);
super.vfunc_map();
}
});
var NotificationSection = GObject.registerClass(
class NotificationSection extends MessageList.MessageListSection {
constructor() {
super();
_init() {
super._init();
this._sources = new Map();
this._nUrgent = 0;
@ -915,8 +985,6 @@ class NotificationSection extends MessageList.MessageListSection {
Main.messageTray.getSources().forEach(source => {
this._sourceAdded(Main.messageTray, source);
});
this.actor.connect('notify::mapped', this._onMapped.bind(this));
}
get allowed() {
@ -924,24 +992,13 @@ class NotificationSection extends MessageList.MessageListSection {
!Main.sessionMode.isGreeter;
}
_createTimeLabel(datetime) {
let label = new St.Label({ style_class: 'event-time',
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.END });
label.connect('notify::mapped', () => {
if (label.mapped)
label.text = Util.formatTimeSpan(datetime);
});
return label;
}
_sourceAdded(tray, source) {
let obj = {
destroyId: 0,
notificationAddedId: 0,
};
obj.destroyId = source.connect('destroy', source => {
obj.destroyId = source.connect('destroy', () => {
this._onSourceDestroy(source, obj);
});
obj.notificationAddedId = source.connect('notification-added',
@ -952,13 +1009,13 @@ class NotificationSection extends MessageList.MessageListSection {
_onNotificationAdded(source, notification) {
let message = new NotificationMessage(notification);
message.setSecondaryActor(this._createTimeLabel(notification.datetime));
message.setSecondaryActor(new TimeLabel(notification.datetime));
let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL;
let updatedId = notification.connect('updated', () => {
message.setSecondaryActor(this._createTimeLabel(notification.datetime));
this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.actor.mapped);
message.setSecondaryActor(new TimeLabel(notification.datetime));
this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.mapped);
});
let destroyId = notification.connect('destroy', () => {
notification.disconnect(destroyId);
@ -978,7 +1035,7 @@ class NotificationSection extends MessageList.MessageListSection {
}
let index = isUrgent ? 0 : this._nUrgent;
this.addMessageAtIndex(message, index, this.actor.mapped);
this.addMessageAtIndex(message, index, this.mapped);
}
_onSourceDestroy(source, obj) {
@ -988,25 +1045,23 @@ class NotificationSection extends MessageList.MessageListSection {
this._sources.delete(source);
}
_onMapped() {
if (!this.actor.mapped)
return;
for (let message of this._messages.keys())
vfunc_map() {
this._messages.forEach(message => {
if (message.notification.urgency != MessageTray.Urgency.CRITICAL)
message.notification.acknowledged = true;
});
super.vfunc_map();
}
_shouldShow() {
return !this.empty && isToday(this._date);
}
};
var Placeholder = class Placeholder {
constructor() {
this.actor = new St.BoxLayout({ style_class: 'message-list-placeholder',
vertical: true });
});
var Placeholder = GObject.registerClass(
class Placeholder extends St.BoxLayout {
_init() {
super._init({ style_class: 'message-list-placeholder', vertical: true });
this._date = new Date();
let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg');
@ -1015,10 +1070,10 @@ var Placeholder = class Placeholder {
this._otherIcon = new Gio.FileIcon({ file: otherFile });
this._icon = new St.Icon();
this.actor.add_actor(this._icon);
this.add_actor(this._icon);
this._label = new St.Label();
this.actor.add_actor(this._label);
this.add_actor(this._label);
this._sync();
}
@ -1045,48 +1100,56 @@ var Placeholder = class Placeholder {
this._label.text = _("No Events");
}
}
};
});
var CalendarMessageList = class CalendarMessageList {
constructor() {
this.actor = new St.Widget({ style_class: 'message-list',
layout_manager: new Clutter.BinLayout(),
x_expand: true, y_expand: true });
var CalendarMessageList = GObject.registerClass(
class CalendarMessageList extends St.Widget {
_init() {
super._init({
style_class: 'message-list',
layout_manager: new Clutter.BinLayout(),
x_expand: true,
y_expand: true,
});
this._placeholder = new Placeholder();
this.actor.add_actor(this._placeholder.actor);
this.add_actor(this._placeholder);
let box = new St.BoxLayout({ vertical: true,
x_expand: true, y_expand: true });
this.actor.add_actor(box);
this.add_actor(box);
this._scrollView = new St.ScrollView({ style_class: 'vfade',
overlay_scrollbars: true,
x_expand: true, y_expand: true,
x_fill: true, y_fill: true });
this._scrollView = new St.ScrollView({
style_class: 'vfade',
overlay_scrollbars: true,
x_expand: true, y_expand: true,
});
this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
box.add_actor(this._scrollView);
this._clearButton = new St.Button({ style_class: 'message-list-clear-button button',
label: _("Clear"),
can_focus: true });
this._clearButton.set_x_align(Clutter.ActorAlign.END);
this._clearButton = new St.Button({
style_class: 'message-list-clear-button button',
label: _('Clear'),
can_focus: true,
x_align: Clutter.ActorAlign.END,
});
this._clearButton.connect('clicked', () => {
let sections = [...this._sections.keys()];
sections.forEach(s => s.clear());
this._sectionList.get_children().forEach(s => s.clear());
});
box.add_actor(this._clearButton);
this._placeholder.actor.bind_property('visible',
this._placeholder.bind_property('visible',
this._clearButton, 'visible',
GObject.BindingFlags.INVERT_BOOLEAN);
this._sectionList = new St.BoxLayout({ style_class: 'message-list-sections',
vertical: true,
x_expand: true,
y_expand: true,
y_align: Clutter.ActorAlign.START });
this._sectionList.connect('actor-added', this._sync.bind(this));
this._sectionList.connect('actor-removed', this._sync.bind(this));
this._scrollView.add_actor(this._sectionList);
this._sections = new Map();
this._mediaSection = new Mpris.MediaSection();
this._addSection(this._mediaSection);
@ -1101,58 +1164,35 @@ var CalendarMessageList = class CalendarMessageList {
}
_addSection(section) {
let obj = {
destroyId: 0,
visibleId: 0,
emptyChangedId: 0,
canClearChangedId: 0,
keyFocusId: 0
};
obj.destroyId = section.actor.connect('destroy', () => {
this._removeSection(section);
});
obj.visibleId = section.actor.connect('notify::visible',
this._sync.bind(this));
obj.emptyChangedId = section.connect('empty-changed',
this._sync.bind(this));
obj.canClearChangedId = section.connect('can-clear-changed',
this._sync.bind(this));
obj.keyFocusId = section.connect('key-focus-in',
this._onKeyFocusIn.bind(this));
let connectionsIds = [];
this._sections.set(section, obj);
this._sectionList.add_actor(section.actor);
this._sync();
}
for (let prop of ['visible', 'empty', 'can-clear']) {
connectionsIds.push(
section.connect(`notify::${prop}`, this._sync.bind(this)));
}
connectionsIds.push(section.connect('message-focused', (_s, messageActor) => {
Util.ensureActorVisibleInScrollView(this._scrollView, messageActor);
}));
_removeSection(section) {
let obj = this._sections.get(section);
section.actor.disconnect(obj.destroyId);
section.actor.disconnect(obj.visibleId);
section.disconnect(obj.emptyChangedId);
section.disconnect(obj.canClearChangedId);
section.disconnect(obj.keyFocusId);
connectionsIds.push(section.connect('destroy', () => {
connectionsIds.forEach(id => section.disconnect(id));
this._sectionList.remove_actor(section);
}));
this._sections.delete(section);
this._sectionList.remove_actor(section.actor);
this._sync();
}
_onKeyFocusIn(section, actor) {
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
this._sectionList.add_actor(section);
}
_sync() {
let sections = [...this._sections.keys()];
let sections = this._sectionList.get_children();
let visible = sections.some(s => s.allowed);
this.actor.visible = visible;
this.visible = visible;
if (!visible)
return;
let empty = sections.every(s => s.empty || !s.actor.visible);
this._placeholder.actor.visible = empty;
let empty = sections.every(s => s.empty || !s.visible);
this._placeholder.visible = empty;
let canClear = sections.some(s => s.canClear && s.actor.visible);
let canClear = sections.some(s => s.canClear && s.visible);
this._clearButton.reactive = canClear;
}
@ -1161,8 +1201,7 @@ var CalendarMessageList = class CalendarMessageList {
}
setDate(date) {
for (let section of this._sections.keys())
section.setDate(date);
this._sectionList.get_children().forEach(s => s.setDate(date));
this._placeholder.setDate(date);
}
};
});

View File

@ -1,19 +1,22 @@
/* exported CheckBox */
const { Clutter, Pango, St } = imports.gi;
const { Clutter, GObject, Pango, St } = imports.gi;
var CheckBox = class CheckBox {
constructor(label) {
let container = new St.BoxLayout();
this.actor = new St.Button({ style_class: 'check-box',
child: container,
button_mask: St.ButtonMask.ONE,
toggle_mode: true,
can_focus: true,
x_fill: true,
y_fill: true });
var CheckBox = GObject.registerClass(
class CheckBox extends St.Button {
_init(label) {
let container = new St.BoxLayout({
x_expand: true,
y_expand: true,
});
super._init({
style_class: 'check-box',
child: container,
button_mask: St.ButtonMask.ONE,
toggle_mode: true,
can_focus: true,
});
this._box = new St.Bin();
this._box.set_y_align(Clutter.ActorAlign.START);
this._box = new St.Bin({ y_align: Clutter.ActorAlign.START });
container.add_actor(this._box);
this._label = new St.Label();
@ -32,4 +35,4 @@ var CheckBox = class CheckBox {
getLabelActor() {
return this._label;
}
};
});

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported CloseDialog */
const { Clutter, Gio, GLib, GObject, Meta, Shell } = imports.gi;
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
const Dialog = imports.ui.dialog;
const Main = imports.ui.main;
@ -13,7 +13,7 @@ var ALIVE_TIMEOUT = 5000;
var CloseDialog = GObject.registerClass({
Implements: [Meta.CloseDialog],
Properties: {
'window': GObject.ParamSpec.override('window', Meta.CloseDialog)
'window': GObject.ParamSpec.override('window', Meta.CloseDialog),
},
}, class CloseDialog extends GObject.Object {
_init(window) {
@ -46,6 +46,18 @@ var CloseDialog = GObject.registerClass({
return new Dialog.MessageDialogContent({ icon, title, subtitle });
}
_updateScale() {
// Since this is a child of MetaWindowActor (which, for Wayland clients,
// applies the geometry scale factor to its children itself, see
// meta_window_actor_set_geometry_scale()), make sure we don't apply
// the factor twice in the end.
if (this._window.get_client_type() !== Meta.WindowClientType.WAYLAND)
return;
let { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
this._dialog.set_scale(1 / scaleFactor, 1 / scaleFactor);
}
_initDialog() {
if (this._dialog)
return;
@ -61,9 +73,14 @@ var CloseDialog = GObject.registerClass({
default: true });
this._dialog.addButton({ label: _('Wait'),
action: this._onWait.bind(this),
key: Clutter.Escape });
key: Clutter.KEY_Escape });
global.focus_manager.add_group(this._dialog);
let themeContext = St.ThemeContext.get_for_stage(global.stage);
themeContext.connect('notify::scale-factor', this._updateScale.bind(this));
this._updateScale();
}
_addWindowEffect() {
@ -107,11 +124,12 @@ var CloseDialog = GObject.registerClass({
if (this._tracked === shouldTrack)
return;
if (shouldTrack)
if (shouldTrack) {
Main.layoutManager.trackChrome(this._dialog,
{ affectsInputRegion: true });
else
} else {
Main.layoutManager.untrackChrome(this._dialog);
}
// The buttons are broken when they aren't added to the input region,
// so disable them properly in that case
@ -145,14 +163,14 @@ var CloseDialog = GObject.registerClass({
this._addWindowEffect();
this._initDialog();
this._dialog.scale_y = 0;
this._dialog.set_pivot_point(0.5, 0.5);
this._dialog._dialog.scale_y = 0;
this._dialog._dialog.set_pivot_point(0.5, 0.5);
this._dialog.ease({
this._dialog._dialog.ease({
scale_y: 1,
mode: Clutter.AnimationMode.LINEAR,
duration: DIALOG_TRANSITION_TIME,
onComplete: this._onFocusChanged.bind(this)
onComplete: this._onFocusChanged.bind(this),
});
}
@ -175,11 +193,11 @@ var CloseDialog = GObject.registerClass({
this._dialog = null;
this._removeWindowEffect();
dialog.ease({
dialog._dialog.ease({
scale_y: 0,
mode: Clutter.AnimationMode.LINEAR,
duration: DIALOG_TRANSITION_TIME,
onComplete: () => dialog.destroy()
onComplete: () => dialog.destroy(),
});
}

View File

@ -58,9 +58,8 @@ var AutomountManager = class {
_InhibitorsChanged(_object, _senderName, [_inhibitor]) {
this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT,
(result, error) => {
if (!error) {
if (!error)
this._inhibited = result[0];
}
});
}
@ -110,7 +109,7 @@ var AutomountManager = class {
// mount operation object
if (drive.can_stop()) {
drive.stop(Gio.MountUnmountFlags.FORCE, null, null,
(drive, res) => {
(o, res) => {
try {
drive.stop_finish(res);
} catch (e) {
@ -119,7 +118,7 @@ var AutomountManager = class {
});
} else if (drive.can_eject()) {
drive.eject_with_operation(Gio.MountUnmountFlags.FORCE, null, null,
(drive, res) => {
(o, res) => {
try {
drive.eject_with_operation_finish(res);
} catch (e) {
@ -223,7 +222,7 @@ var AutomountManager = class {
delete volume._allowAutorunExpireId;
}
this._volumeQueue =
this._volumeQueue.filter(element => (element != volume));
this._volumeQueue.filter(element => element != volume);
}
_reaskPassword(volume) {
@ -231,7 +230,7 @@ var AutomountManager = class {
let existingDialog = prevOperation ? prevOperation.borrowDialog() : null;
let operation =
new ShellMountOperation.ShellMountOperation(volume,
{ existingDialog: existingDialog });
{ existingDialog });
this._mountVolume(volume, operation);
}

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Component */
const { Gio, St } = imports.gi;
const { Clutter, Gio, GObject, St } = imports.gi;
const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
@ -20,7 +20,7 @@ var AutorunSetting = {
RUN: 0,
IGNORE: 1,
FILES: 2,
ASK: 3
ASK: 3,
};
// misc utils
@ -41,7 +41,7 @@ function isMountRootHidden(root) {
let path = root.get_path();
// skip any mounts in hidden directory hierarchies
return (path.includes('/.'));
return path.includes('/.');
}
function isMountNonLocal(mount) {
@ -52,7 +52,7 @@ function isMountNonLocal(mount) {
if (volume == null)
return true;
return (volume.get_identifier("class") == "network");
return volume.get_identifier("class") == "network";
}
function startAppForMount(app, mount) {
@ -115,7 +115,8 @@ var ContentTypeDiscoverer = class {
let hotplugSniffer = new HotplugSniffer();
hotplugSniffer.SniffURIRemote(root.get_uri(),
([contentTypes]) => {
result => {
[contentTypes] = result;
this._emitCallback(mount, contentTypes);
});
}
@ -124,7 +125,7 @@ var ContentTypeDiscoverer = class {
_emitCallback(mount, contentTypes = []) {
// we're not interested in win32 software content types here
contentTypes = contentTypes.filter(
type => (type != 'x-content/win32-software')
type => type != 'x-content/win32-software'
);
let apps = [];
@ -166,7 +167,7 @@ var AutorunManager = class {
if (!this._session.SessionIsActive)
return;
let discoverer = new ContentTypeDiscoverer((mount, apps, contentTypes) => {
let discoverer = new ContentTypeDiscoverer((m, apps, contentTypes) => {
this._dispatcher.addMount(mount, apps, contentTypes);
});
discoverer.guessContentTypes(mount);
@ -201,7 +202,7 @@ var AutorunDispatcher = class {
}
_getSourceForMount(mount) {
let filtered = this._sources.filter(source => (source.mount == mount));
let filtered = this._sources.filter(source => source.mount == mount);
// we always make sure not to add two sources for the same
// mount in addMount(), so it's safe to assume filtered.length
@ -245,11 +246,10 @@ var AutorunDispatcher = class {
let success = false;
let app = null;
if (setting == AutorunSetting.RUN) {
if (setting == AutorunSetting.RUN)
app = Gio.app_info_get_default_for_type(contentTypes[0], false);
} else if (setting == AutorunSetting.FILES) {
else if (setting == AutorunSetting.FILES)
app = Gio.app_info_get_default_for_type('inode/directory', false);
}
if (app)
success = startAppForMount(app, mount);
@ -272,9 +272,10 @@ var AutorunDispatcher = class {
}
};
var AutorunSource = class extends MessageTray.Source {
constructor(manager, mount, apps) {
super(mount.get_name());
var AutorunSource = GObject.registerClass(
class AutorunSource extends MessageTray.Source {
_init(manager, mount, apps) {
super._init(mount.get_name());
this._manager = manager;
this.mount = mount;
@ -284,7 +285,7 @@ var AutorunSource = class extends MessageTray.Source {
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
this.showNotification(this._notification);
}
getIcon() {
@ -294,11 +295,12 @@ var AutorunSource = class extends MessageTray.Source {
_createPolicy() {
return new MessageTray.NotificationApplicationPolicy('org.gnome.Nautilus');
}
};
});
var AutorunNotification = class extends MessageTray.Notification {
constructor(manager, source) {
super(source, source.title);
var AutorunNotification = GObject.registerClass(
class AutorunNotification extends MessageTray.Notification {
_init(manager, source) {
super._init(source, source.title);
this._manager = manager;
this._mount = source.mount;
@ -318,20 +320,23 @@ var AutorunNotification = class extends MessageTray.Notification {
}
_buttonForApp(app) {
let box = new St.BoxLayout();
let box = new St.BoxLayout({
x_expand: true,
x_align: Clutter.ActorAlign.START,
});
let icon = new St.Icon({ gicon: app.get_icon(),
style_class: 'hotplug-notification-item-icon' });
box.add(icon);
let label = new St.Bin({
y_align: St.Align.MIDDLE,
child: new St.Label({ text: _("Open with %s").format(app.get_name()) }),
child: new St.Label({
text: _("Open with %s").format(app.get_name()),
y_align: Clutter.ActorAlign.CENTER,
}),
});
box.add(label);
let button = new St.Button({ child: box,
x_fill: true,
x_align: St.Align.START,
x_expand: true,
button_mask: St.ButtonMask.ONE,
style_class: 'hotplug-notification-item button' });
@ -350,6 +355,6 @@ var AutorunNotification = class extends MessageTray.Notification {
let app = Gio.app_info_get_default_for_type('inode/directory', false);
startAppForMount(app, this._mount);
}
};
});
var Component = AutorunManager;

View File

@ -33,7 +33,7 @@ class KeyringDialog extends ModalDialog.ModalDialog {
this._cancelButton = this.addButton({ label: '',
action: this._onCancelButton.bind(this),
key: Clutter.Escape });
key: Clutter.KEY_Escape });
this._continueButton = this.addButton({ label: '',
action: this._onContinueButton.bind(this),
default: true });
@ -54,8 +54,12 @@ class KeyringDialog extends ModalDialog.ModalDialog {
_buildControlTable() {
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
layout_manager: layout });
let table = new St.Widget({
style_class: 'keyring-dialog-control-table',
layout_manager: layout,
x_expand: true,
y_expand: true,
});
layout.hookup_style(table);
let rtl = table.get_text_direction() == Clutter.TextDirection.RTL;
let row = 0;
@ -74,16 +78,18 @@ class KeyringDialog extends ModalDialog.ModalDialog {
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', this._onPasswordActivate.bind(this));
this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, true);
this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, {
animate: true,
});
if (rtl) {
layout.attach(this._workSpinner.actor, 0, row, 1, 1);
layout.attach(this._workSpinner, 0, row, 1, 1);
layout.attach(this._passwordEntry, 1, row, 1, 1);
layout.attach(label, 2, row, 1, 1);
} else {
layout.attach(label, 0, row, 1, 1);
layout.attach(this._passwordEntry, 1, row, 1, 1);
layout.attach(this._workSpinner.actor, 2, row, 1, 1);
layout.attach(this._workSpinner, 2, row, 1, 1);
}
row++;
} else {
@ -92,9 +98,9 @@ class KeyringDialog extends ModalDialog.ModalDialog {
}
if (this.prompt.confirm_visible) {
var label = new St.Label(({ style_class: 'prompt-dialog-password-label',
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.CENTER }));
var label = new St.Label({ style_class: 'prompt-dialog-password-label',
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.CENTER });
label.set_text(_("Type again:"));
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '',
@ -121,8 +127,8 @@ class KeyringDialog extends ModalDialog.ModalDialog {
if (this.prompt.choice_visible) {
let choice = new CheckBox.CheckBox();
this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
layout.attach(choice.actor, rtl ? 0 : 1, row, 1, 1);
this.prompt.bind_property('choice-chosen', choice, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
layout.attach(choice, rtl ? 0 : 1, row, 1, 1);
row++;
}
@ -140,7 +146,7 @@ class KeyringDialog extends ModalDialog.ModalDialog {
}
this._controlTable = table;
this._content.messageBox.add(table, { x_fill: true, y_fill: true });
this._content.messageBox.add_child(table);
}
_updateSensitivity(sensitive) {
@ -228,10 +234,11 @@ var KeyringDummyDialog = class {
}
};
var KeyringPrompter = class {
constructor() {
this._prompter = new Gcr.SystemPrompter();
this._prompter.connect('new-prompt', () => {
var KeyringPrompter = GObject.registerClass(
class KeyringPrompter extends Gcr.SystemPrompter {
_init() {
super._init();
this.connect('new-prompt', () => {
let dialog = this._enabled
? new KeyringDialog()
: new KeyringDummyDialog();
@ -246,7 +253,7 @@ var KeyringPrompter = class {
enable() {
if (!this._registered) {
this._prompter.register(Gio.DBus.session);
this.register(Gio.DBus.session);
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
this._registered = true;
@ -257,10 +264,10 @@ var KeyringPrompter = class {
disable() {
this._enabled = false;
if (this._prompter.prompting)
if (this.prompting)
this._currentPrompt.cancel();
this._currentPrompt = null;
}
};
});
var Component = KeyringPrompter;

View File

@ -56,7 +56,7 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog {
secret.entry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: secret.value, can_focus: reactive,
reactive: reactive,
reactive,
x_expand: true });
ShellEntry.addContextMenu(secret.entry,
{ isPassword: secret.password });
@ -106,10 +106,7 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog {
descriptionLabel.clutter_text.line_wrap = true;
descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
contentBox.messageBox.add(descriptionLabel,
{ y_fill: true,
y_align: St.Align.START,
expand: true });
contentBox.messageBox.add_child(descriptionLabel);
}
this._okButton = {
@ -172,7 +169,7 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog {
return true;
}
return (value.length >= 8 && value.length <= 63);
return value.length >= 8 && value.length <= 63;
}
_validateStaticWep(secret) {
@ -225,11 +222,12 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog {
validate: this._validateStaticWep, password: true });
break;
case 'ieee8021x':
if (wirelessSecuritySetting.auth_alg == 'leap') // Cisco LEAP
if (wirelessSecuritySetting.auth_alg == 'leap') { // Cisco LEAP
secrets.push({ label: _("Password: "), key: 'leap-password',
value: wirelessSecuritySetting.leap_password || '', password: true });
else // Dynamic (IEEE 802.1x) WEP
} else { // Dynamic (IEEE 802.1x) WEP
this._get8021xSecrets(secrets);
}
break;
case 'wpa-eap':
this._get8021xSecrets(secrets);
@ -244,15 +242,18 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog {
/* If hints were given we know exactly what we need to ask */
if (this._settingName == "802-1x" && this._hints.length) {
if (this._hints.includes('identity'))
if (this._hints.includes('identity')) {
secrets.push({ label: _("Username: "), key: 'identity',
value: ieee8021xSetting.identity || '', password: false });
if (this._hints.includes('password'))
}
if (this._hints.includes('password')) {
secrets.push({ label: _("Password: "), key: 'password',
value: ieee8021xSetting.password || '', password: true });
if (this._hints.includes('private-key-password'))
}
if (this._hints.includes('private-key-password')) {
secrets.push({ label: _("Private key password: "), key: 'private-key-password',
value: ieee8021xSetting.private_key_password || '', password: true });
}
return;
}
@ -556,7 +557,7 @@ var VPNRequestHandler = class {
contentOverride.secrets.push({
label: keyfile.get_string(groups[i], 'Label'),
key: groups[i],
value: value,
value,
password: keyfile.get_boolean(groups[i], 'IsSecret'),
});
} else {
@ -734,7 +735,7 @@ var NetworkAgent = class {
});
Main.messageTray.add(source);
source.notify(notification);
source.showNotification(notification);
}
_newRequest(agent, requestId, connection, settingName, hints, flags) {

View File

@ -11,12 +11,19 @@ const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const UserWidget = imports.ui.userWidget;
const DialogMode = {
AUTH: 0,
CONFIRM: 1,
};
var DIALOG_ICON_SIZE = 48;
var WORK_SPINNER_ICON_SIZE = 16;
const DELAYED_RESET_TIMEOUT = 200;
var AuthenticationDialog = GObject.registerClass({
Signals: { 'done': { param_types: [GObject.TYPE_BOOLEAN] } }
Signals: { 'done': { param_types: [GObject.TYPE_BOOLEAN] } },
}, class AuthenticationDialog extends ModalDialog.ModalDialog {
_init(actionId, body, cookie, userNames) {
super._init({ styleClass: 'prompt-dialog' });
@ -24,7 +31,6 @@ var AuthenticationDialog = GObject.registerClass({
this.actionId = actionId;
this.message = body;
this.userNames = userNames;
this._wasDismissed = false;
this._sessionUpdatedId = Main.sessionMode.connect('updated', () => {
this.visible = !Main.sessionMode.isLocked;
@ -51,70 +57,63 @@ var AuthenticationDialog = GObject.registerClass({
userName = userNames[0];
this._user = AccountsService.UserManager.get_default().get_user(userName);
let userRealName = this._user.get_real_name();
this._userLoadedId = this._user.connect('notify::is_loaded',
this._onUserChanged.bind(this));
this._userChangedId = this._user.connect('changed',
this._onUserChanged.bind(this));
// Special case 'root'
let userIsRoot = false;
if (userName == 'root') {
userIsRoot = true;
userRealName = _("Administrator");
}
let userBox = new St.BoxLayout({
style_class: 'polkit-dialog-user-layout',
vertical: false,
});
content.messageBox.add(userBox);
if (userIsRoot) {
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
text: userRealName }));
content.messageBox.add(userLabel, { x_fill: false,
x_align: St.Align.START });
} else {
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false });
content.messageBox.add(userBox);
this._userAvatar = new UserWidget.Avatar(this._user,
{ iconSize: DIALOG_ICON_SIZE,
styleClass: 'polkit-dialog-user-icon' });
this._userAvatar.actor.hide();
userBox.add(this._userAvatar.actor,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-label',
text: userRealName }));
userBox.add(userLabel,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
}
this._userAvatar = new UserWidget.Avatar(this._user, {
iconSize: DIALOG_ICON_SIZE,
styleClass: 'polkit-dialog-user-icon',
});
userBox.add_child(this._userAvatar);
this._onUserChanged();
this._userLabel = new St.Label({
style_class: userName === 'root'
? 'polkit-dialog-user-root-label'
: 'polkit-dialog-user-label',
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
});
if (userName === 'root')
this._userLabel.text = _('Administrator');
userBox.add_child(this._userLabel);
this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' });
content.messageBox.add(this._passwordBox);
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: "",
can_focus: true });
this._passwordLabel = new St.Label({
style_class: 'prompt-dialog-password-label',
y_align: Clutter.ActorAlign.CENTER,
});
this._passwordBox.add_child(this._passwordLabel);
this._passwordEntry = new St.Entry({
style_class: 'prompt-dialog-password-entry',
text: "",
can_focus: true,
x_expand: true,
});
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
this._passwordBox.add(this._passwordEntry,
{ expand: true });
this._passwordEntry.bind_property('reactive',
this._passwordEntry.clutter_text, 'editable',
GObject.BindingFlags.SYNC_CREATE);
this._passwordBox.add_child(this._passwordEntry);
this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, true);
this._passwordBox.add(this._workSpinner.actor);
this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, {
animate: true,
});
this._passwordBox.add(this._workSpinner);
this.setInitialKeyFocus(this._passwordEntry);
this._passwordBox.hide();
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._errorMessageLabel.clutter_text.line_wrap = true;
content.messageBox.add(this._errorMessageLabel, { x_fill: false, x_align: St.Align.START });
content.messageBox.add_child(this._errorMessageLabel);
this._errorMessageLabel.hide();
this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });
@ -137,15 +136,30 @@ var AuthenticationDialog = GObject.registerClass({
this._cancelButton = this.addButton({ label: _("Cancel"),
action: this.cancel.bind(this),
key: Clutter.Escape });
key: Clutter.KEY_Escape });
this._okButton = this.addButton({ label: _("Authenticate"),
action: this._onAuthenticateButtonPressed.bind(this),
default: true });
reactive: false });
this._okButton.bind_property('reactive',
this._okButton, 'can-focus',
GObject.BindingFlags.SYNC_CREATE);
this._passwordEntry.clutter_text.connect('text-changed', text => {
this._okButton.reactive = text.get_text().length > 0;
});
this._doneEmitted = false;
this._mode = -1;
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
this._cookie = cookie;
this._userLoadedId = this._user.connect('notify::is-loaded',
this._onUserChanged.bind(this));
this._userChangedId = this._user.connect('changed',
this._onUserChanged.bind(this));
this._onUserChanged();
}
_setWorking(working) {
@ -155,8 +169,9 @@ var AuthenticationDialog = GObject.registerClass({
this._workSpinner.stop();
}
performAuthentication() {
this._destroySession();
_initiateSession() {
this._destroySession(DELAYED_RESET_TIMEOUT);
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
cookie: this._cookie });
this._sessionCompletedId = this._session.connect('completed', this._onSessionCompleted.bind(this));
@ -195,18 +210,15 @@ var AuthenticationDialog = GObject.registerClass({
}
}
_updateSensitivity(sensitive) {
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
this._okButton.can_focus = sensitive;
this._okButton.reactive = sensitive;
this._setWorking(!sensitive);
}
_onEntryActivate() {
let response = this._passwordEntry.get_text();
this._updateSensitivity(false);
if (response.length === 0)
return;
this._passwordEntry.reactive = false;
this._okButton.reactive = false;
this._setWorking(true);
this._session.response(response);
// When the user responds, dismiss already shown info and
// error texts (if any)
@ -216,7 +228,10 @@ var AuthenticationDialog = GObject.registerClass({
}
_onAuthenticateButtonPressed() {
this._onEntryActivate();
if (this._mode === DialogMode.CONFIRM)
this._initiateSession();
else
this._onEntryActivate();
}
_onSessionCompleted(session, gainedAuthorization) {
@ -235,7 +250,7 @@ var AuthenticationDialog = GObject.registerClass({
* error providing authentication-method specific information),
* show "Sorry, that didn't work. Please try again."
*/
if (!this._errorMessageLabel.visible && !this._wasDismissed) {
if (!this._errorMessageLabel.visible) {
/* Translators: "that didn't work" refers to the fact that the
* requested authentication was not gained; this can happen
* because of an authentication error (like invalid password),
@ -247,11 +262,16 @@ var AuthenticationDialog = GObject.registerClass({
}
/* Try and authenticate again */
this.performAuthentication();
this._initiateSession();
}
}
_onSessionRequest(session, request, echoOn) {
if (this._sessionRequestTimeoutId) {
GLib.source_remove(this._sessionRequestTimeoutId);
this._sessionRequestTimeoutId = 0;
}
// Cheap localization trick
if (request == 'Password:' || request == 'Password: ')
this._passwordLabel.set_text(_("Password:"));
@ -265,9 +285,12 @@ var AuthenticationDialog = GObject.registerClass({
this._passwordBox.show();
this._passwordEntry.set_text('');
this._passwordEntry.grab_key_focus();
this._updateSensitivity(true);
this._passwordEntry.reactive = true;
this._okButton.reactive = false;
this._setWorking(false);
this._ensureOpen();
this._passwordEntry.grab_key_focus();
}
_onSessionShowError(session, text) {
@ -288,29 +311,78 @@ var AuthenticationDialog = GObject.registerClass({
this._ensureOpen();
}
_destroySession() {
_destroySession(delay = 0) {
if (this._session) {
if (!this._completed)
this._session.cancel();
this._completed = false;
this._session.disconnect(this._sessionCompletedId);
this._session.disconnect(this._sessionRequestId);
this._session.disconnect(this._sessionShowErrorId);
this._session.disconnect(this._sessionShowInfoId);
if (!this._completed)
this._session.cancel();
this._completed = false;
this._session = null;
}
if (this._sessionRequestTimeoutId) {
GLib.source_remove(this._sessionRequestTimeoutId);
this._sessionRequestTimeoutId = 0;
}
let resetDialog = () => {
if (this.state != ModalDialog.State.OPENED)
return;
this._passwordBox.hide();
this._cancelButton.grab_key_focus();
this._okButton.reactive = false;
};
if (delay) {
this._sessionRequestTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, resetDialog);
GLib.Source.set_name_by_id(this._sessionRequestTimeoutId, '[gnome-shell] this._sessionRequestTimeoutId');
} else {
resetDialog();
}
}
_onUserChanged() {
if (this._user.is_loaded && this._userAvatar) {
this._userAvatar.update();
this._userAvatar.actor.show();
if (!this._user.is_loaded)
return;
let userName = this._user.get_user_name();
let realName = this._user.get_real_name();
if (userName !== 'root')
this._userLabel.set_text(realName);
this._userAvatar.update();
if (this._user.get_password_mode() === AccountsService.UserPasswordMode.NONE) {
if (this._mode === DialogMode.CONFIRM)
return;
this._mode = DialogMode.CONFIRM;
this._destroySession();
this._okButton.reactive = true;
/* We normally open the dialog when we get a "request" signal, but
* since in this case initiating a session would perform the
* authentication, only open the dialog and initiate the session
* when the user confirmed. */
this._ensureOpen();
} else {
if (this._mode === DialogMode.AUTH)
return;
this._mode = DialogMode.AUTH;
this._initiateSession();
}
}
cancel() {
this._wasDismissed = true;
this.close(global.get_current_time());
this._emitDone(true);
}
@ -318,7 +390,10 @@ var AuthenticationDialog = GObject.registerClass({
_onDialogClosed() {
if (this._sessionUpdatedId)
Main.sessionMode.disconnect(this._sessionUpdatedId);
this._sessionUpdatedId = 0;
if (this._sessionRequestTimeoutId)
GLib.source_remove(this._sessionRequestTimeoutId);
this._sessionRequestTimeoutId = 0;
if (this._user) {
this._user.disconnect(this._userLoadedId);
@ -330,19 +405,20 @@ var AuthenticationDialog = GObject.registerClass({
}
});
var AuthenticationAgent = class {
constructor() {
var AuthenticationAgent = GObject.registerClass(
class AuthenticationAgent extends Shell.PolkitAuthenticationAgent {
_init() {
super._init();
this._currentDialog = null;
this._handle = null;
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', this._onInitiate.bind(this));
this._native.connect('cancel', this._onCancel.bind(this));
this.connect('initiate', this._onInitiate.bind(this));
this.connect('cancel', this._onCancel.bind(this));
this._sessionUpdatedId = 0;
}
enable() {
try {
this._native.register();
this.register();
} catch (e) {
log('Failed to register AuthenticationAgent');
}
@ -350,7 +426,7 @@ var AuthenticationAgent = class {
disable() {
try {
this._native.unregister();
this.unregister();
} catch (e) {
log('Failed to unregister AuthenticationAgent');
}
@ -369,19 +445,7 @@ var AuthenticationAgent = class {
}
this._currentDialog = new AuthenticationDialog(actionId, message, cookie, userNames);
// We actually don't want to open the dialog until we know for
// sure that we're going to interact with the user. For
// example, if the password for the identity to auth is blank
// (which it will be on a live CD) then there will be no
// conversation at all... of course, we don't *know* that
// until we actually try it.
//
// See https://bugzilla.gnome.org/show_bug.cgi?id=643062 for more
// discussion.
this._currentDialog.connect('done', this._onDialogDone.bind(this));
this._currentDialog.performAuthentication();
}
_onCancel(_nativeAgent) {
@ -400,8 +464,8 @@ var AuthenticationAgent = class {
Main.sessionMode.disconnect(this._sessionUpdatedId);
this._sessionUpdatedId = 0;
this._native.complete(dismissed);
this.complete(dismissed);
}
};
});
var Component = AuthenticationAgent;

View File

@ -19,7 +19,7 @@ const MessageTray = imports.ui.messageTray;
const Params = imports.misc.params;
const Util = imports.misc.util;
const HAVE_TP = (Tp != null && Tpl != null);
const HAVE_TP = Tp != null && Tpl != null;
// See Notification.appendMessage
var SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
@ -37,7 +37,7 @@ var CHAT_EXPAND_LINES = 12;
var NotificationDirection = {
SENT: 'chat-sent',
RECEIVED: 'chat-received'
RECEIVED: 'chat-received',
};
function makeMessageFromTpMessage(tpMessage, direction) {
@ -49,10 +49,10 @@ function makeMessageFromTpMessage(tpMessage, direction) {
return {
messageType: tpMessage.get_message_type(),
text: text,
text,
sender: tpMessage.sender.alias,
timestamp: timestamp,
direction: direction
timestamp,
direction,
};
}
@ -66,7 +66,7 @@ function makeMessageFromTplEvent(event) {
text: event.get_message(),
sender: event.get_sender().get_alias(),
timestamp: event.get_timestamp(),
direction: direction
direction,
};
}
@ -158,7 +158,7 @@ class TelepathyClient extends Tp.BaseClient {
continue;
/* Only observe contact text channels */
if ((!(channel instanceof Tp.TextChannel)) ||
if (!(channel instanceof Tp.TextChannel) ||
targetHandleType != Tp.HandleType.CONTACT)
continue;
@ -215,7 +215,7 @@ class TelepathyClient extends Tp.BaseClient {
// We are already handling the channel, display the source
let source = this._chatSources[channel.get_object_path()];
if (source)
source.notify();
source.showNotification();
}
}
}
@ -231,11 +231,12 @@ class TelepathyClient extends Tp.BaseClient {
return;
}
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT) {
this._approveTextChannel(account, conn, channel, dispatchOp, context);
else
} else {
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
message: 'Unsupported channel type' }));
}
}
_approveTextChannel(account, conn, channel, dispatchOp, context) {
@ -248,7 +249,7 @@ class TelepathyClient extends Tp.BaseClient {
}
// Approve private text channels right away as we are going to handle it
dispatchOp.claim_with_async(this, (dispatchOp, result) => {
dispatchOp.claim_with_async(this, (o, result) => {
try {
dispatchOp.claim_with_finish(result);
this._handlingChannels(account, conn, [channel], false);
@ -266,9 +267,10 @@ class TelepathyClient extends Tp.BaseClient {
}
}) : null;
var ChatSource = class extends MessageTray.Source {
constructor(account, conn, channel, contact, client) {
super(contact.get_alias());
var ChatSource = HAVE_TP ? GObject.registerClass(
class ChatSource extends MessageTray.Source {
_init(account, conn, channel, contact, client) {
super._init(contact.get_alias());
this._account = account;
this._contact = contact;
@ -326,7 +328,7 @@ var ChatSource = class extends MessageTray.Source {
// We ack messages when the user expands the new notification
let id = this._banner.connect('expanded', this._ackMessages.bind(this));
this._banner.actor.connect('destroy', () => {
this._banner.connect('destroy', () => {
this._banner.disconnect(id);
this._banner = null;
});
@ -348,11 +350,10 @@ var ChatSource = class extends MessageTray.Source {
getIcon() {
let file = this._contact.get_avatar_file();
if (file) {
return new Gio.FileIcon({ file: file });
} else {
if (file)
return new Gio.FileIcon({ file });
else
return new Gio.ThemedIcon({ name: 'avatar-default' });
}
}
getSecondaryIcon() {
@ -386,10 +387,11 @@ var ChatSource = class extends MessageTray.Source {
_updateAvatarIcon() {
this.iconUpdated();
if (this._notifiction)
if (this._notifiction) {
this._notification.update(this._notification.title,
this._notification.bannerBodyText,
{ gicon: this.getIcon() });
}
}
open() {
@ -476,7 +478,7 @@ var ChatSource = class extends MessageTray.Source {
this._notification.appendMessage(pendingMessages[i], true);
if (pendingMessages.length > 0)
this.notify();
this.showNotification();
}
destroy(reason) {
@ -553,7 +555,7 @@ var ChatSource = class extends MessageTray.Source {
_notifyTimeout() {
if (this._pendingMessages.length != 0)
this.notify();
this.showNotification();
this._notifyTimeoutId = 0;
@ -568,8 +570,8 @@ var ChatSource = class extends MessageTray.Source {
this._notification.appendMessage(message);
}
notify() {
super.notify(this._notification);
showNotification() {
super.showNotification(this._notification);
}
respond(text) {
@ -601,10 +603,11 @@ var ChatSource = class extends MessageTray.Source {
}
_presenceChanged(_contact, _presence, _status, _message) {
if (this._notification)
if (this._notification) {
this._notification.update(this._notification.title,
this._notification.bannerBodyText,
{ secondaryGIcon: this.getSecondaryIcon() });
}
}
_pendingRemoved(channel, message) {
@ -625,12 +628,18 @@ var ChatSource = class extends MessageTray.Source {
// 'pending-message-removed' for each one.
this._channel.ack_all_pending_messages_async(null);
}
};
}) : null;
var ChatNotification = class extends MessageTray.Notification {
constructor(source) {
super(source, source.title, null,
{ secondaryGIcon: source.getSecondaryIcon() });
var ChatNotification = HAVE_TP ? GObject.registerClass({
Signals: {
'message-removed': { param_types: [Tp.Message.$gtype] },
'message-added': { param_types: [Tp.Message.$gtype] },
'timestamp-changed': { param_types: [Tp.Message.$gtype] },
},
}, class ChatNotification extends MessageTray.Notification {
_init(source) {
super._init(source, source.title, null,
{ secondaryGIcon: source.getSecondaryIcon() });
this.setUrgency(MessageTray.Urgency.HIGH);
this.setResident(true);
@ -647,16 +656,16 @@ var ChatNotification = class extends MessageTray.Notification {
/**
* appendMessage:
* @message: An object with the properties:
* text: the body of the message,
* messageType: a #Tp.ChannelTextMessageType,
* sender: the name of the sender,
* timestamp: the time the message was sent
* direction: a #NotificationDirection
* @param {Object} message: An object with the properties
* {string} message.text: the body of the message,
* {Tp.ChannelTextMessageType} message.messageType: the type
* {string} message.sender: the name of the sender,
* {number} message.timestamp: the time the message was sent
* {NotificationDirection} message.direction: a #NotificationDirection
*
* @noTimestamp: Whether to add a timestamp. If %true, no timestamp
* will be added, regardless of the difference since the
* last timestamp
* @param {bool} noTimestamp: Whether to add a timestamp. If %true,
* no timestamp will be added, regardless of the difference since
* the last timestamp
*/
appendMessage(message, noTimestamp) {
let messageBody = GLib.markup_escape_text(message.text, -1);
@ -668,19 +677,20 @@ var ChatNotification = class extends MessageTray.Notification {
styles.push('chat-action');
}
if (message.direction == NotificationDirection.RECEIVED)
if (message.direction == NotificationDirection.RECEIVED) {
this.update(this.source.title, messageBody,
{ datetime: GLib.DateTime.new_from_unix_local (message.timestamp),
{ datetime: GLib.DateTime.new_from_unix_local(message.timestamp),
bannerMarkup: true });
}
let group = (message.direction == NotificationDirection.RECEIVED
? 'received' : 'sent');
let group = message.direction == NotificationDirection.RECEIVED
? 'received' : 'sent';
this._append({ body: messageBody,
group: group,
styles: styles,
group,
styles,
timestamp: message.timestamp,
noTimestamp: noTimestamp });
noTimestamp });
}
_filterMessages() {
@ -688,7 +698,7 @@ var ChatNotification = class extends MessageTray.Notification {
return;
let lastMessageTime = this.messages[0].timestamp;
let currentTime = (Date.now() / 1000);
let currentTime = Date.now() / 1000;
// Keep the scrollback from growing too long. If the most
// recent message (before the one we just added) is within
@ -696,7 +706,7 @@ var ChatNotification = class extends MessageTray.Notification {
// SCROLLBACK_RECENT_LENGTH previous messages. Otherwise
// we'll keep SCROLLBACK_IDLE_LENGTH messages.
let maxLength = (lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME)
let maxLength = lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME
? SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH;
let filteredHistory = this.messages.filter(item => item.realMessage);
@ -710,16 +720,16 @@ var ChatNotification = class extends MessageTray.Notification {
/**
* _append:
* @props: An object with the properties:
* body: The text of the message.
* group: The group of the message, one of:
* @param {Object} props: An object with the properties:
* {string} props.body: The text of the message.
* {string} props.group: The group of the message, one of:
* 'received', 'sent', 'meta'.
* styles: Style class names for the message to have.
* timestamp: The timestamp of the message.
* noTimestamp: suppress timestamp signal?
* {string[]} props.styles: Style class names for the message to have.
* {number} props.timestamp: The timestamp of the message.
* {bool} props.noTimestamp: suppress timestamp signal?
*/
_append(props) {
let currentTime = (Date.now() / 1000);
let currentTime = Date.now() / 1000;
props = Params.parse(props, { body: null,
group: null,
styles: [],
@ -782,7 +792,7 @@ var ChatNotification = class extends MessageTray.Notification {
this._filterMessages();
}
};
}) : null;
var ChatLineBox = GObject.registerClass(
class ChatLineBox extends St.BoxLayout {
@ -792,9 +802,10 @@ class ChatLineBox extends St.BoxLayout {
}
});
var ChatNotificationBanner = class extends MessageTray.NotificationBanner {
constructor(notification) {
super(notification);
var ChatNotificationBanner = GObject.registerClass(
class ChatNotificationBanner extends MessageTray.NotificationBanner {
_init(notification) {
super._init(notification);
this._responseEntry = new St.Entry({ style_class: 'chat-response',
x_expand: true,
@ -879,8 +890,7 @@ var ChatNotificationBanner = class extends MessageTray.NotificationBanner {
}
_addMessage(message) {
let highlighter = new MessageList.URLHighlighter(message.body, true, true);
let body = highlighter.actor;
let body = new MessageList.URLHighlighter(message.body, true, true);
let styles = message.styles;
for (let i = 0; i < styles.length; i++)
@ -968,6 +978,6 @@ var ChatNotificationBanner = class extends MessageTray.NotificationBanner {
this.notification.source.setChatState(Tp.ChannelChatState.ACTIVE);
}
}
};
});
var Component = TelepathyComponent;

View File

@ -12,7 +12,7 @@ var POPUP_APPICON_SIZE = 96;
var SortGroup = {
TOP: 0,
MIDDLE: 1,
BOTTOM: 2
BOTTOM: 2,
};
var CtrlAltTabManager = class CtrlAltTabManager {
@ -86,25 +86,26 @@ var CtrlAltTabManager = class CtrlAltTabManager {
for (let i = 0; i < windows.length; i++) {
let icon = null;
let iconName = null;
if (windows[i].get_window_type () == Meta.WindowType.DESKTOP) {
if (windows[i].get_window_type() == Meta.WindowType.DESKTOP) {
iconName = 'video-display-symbolic';
} else {
let app = windowTracker.get_window_app(windows[i]);
if (app)
if (app) {
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
else
} else {
icon = textureCache.bind_cairo_surface_property(windows[i],
'icon',
POPUP_APPICON_SIZE);
}
}
items.push({ name: windows[i].title,
proxy: windows[i].get_compositor_private(),
focusCallback: function(timestamp) {
Main.activateWindow(this, timestamp);
}.bind(windows[i]),
focusCallback: timestamp => {
Main.activateWindow(windows[i], timestamp);
},
iconActor: icon,
iconName: iconName,
iconName,
sortGroup: SortGroup.MIDDLE });
}
}
@ -143,9 +144,9 @@ class CtrlAltTabPopup extends SwitcherPopup.SwitcherPopup {
this._select(this._next());
else if (action == Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD)
this._select(this._previous());
else if (keysym == Clutter.Left)
else if (keysym == Clutter.KEY_Left)
this._select(this._previous());
else if (keysym == Clutter.Right)
else if (keysym == Clutter.KEY_Right)
this._select(this._next());
else
return Clutter.EVENT_PROPAGATE;
@ -177,10 +178,13 @@ class CtrlAltTabSwitcher extends SwitcherPopup.SwitcherList {
icon = new St.Icon({ icon_name: item.iconName,
icon_size: POPUP_APPICON_SIZE });
}
box.add(icon, { x_fill: false, y_fill: false } );
box.add_child(icon);
let text = new St.Label({ text: item.name });
box.add(text, { x_fill: false });
let text = new St.Label({
text: item.name,
x_align: Clutter.ActorAlign.CENTER,
});
box.add_child(text);
this.addItem(box, text);
}

View File

@ -1,8 +1,8 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Dash */
const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
const Signals = imports.signals;
const { Clutter, GLib, GObject,
Graphene, Meta, Shell, St } = imports.gi;
const AppDisplay = imports.ui.appDisplay;
const AppFavorites = imports.ui.appFavorites;
@ -16,18 +16,18 @@ var DASH_ITEM_LABEL_HIDE_TIME = 100;
var DASH_ITEM_HOVER_TIMEOUT = 300;
function getAppFromSource(source) {
if (source instanceof AppDisplay.AppIcon) {
if (source instanceof AppDisplay.AppIcon)
return source.app;
} else {
else
return null;
}
}
var DashIcon = class DashIcon extends AppDisplay.AppIcon {
constructor(app) {
super(app, {
var DashIcon = GObject.registerClass(
class DashIcon extends AppDisplay.AppIcon {
_init(app) {
super._init(app, {
setSizeManually: true,
showLabel: false
showLabel: false,
});
}
@ -45,7 +45,7 @@ var DashIcon = class DashIcon extends AppDisplay.AppIcon {
acceptDrop() {
return false;
}
};
});
// A container like StBin, but taking the child's scale into account
// when requesting a size
@ -53,7 +53,7 @@ var DashItemContainer = GObject.registerClass(
class DashItemContainer extends St.Widget {
_init() {
super._init({ style_class: 'dash-item-container',
pivot_point: new Clutter.Point({ x: .5, y: .5 }),
pivot_point: new Graphene.Point({ x: .5, y: .5 }),
scale_x: 0,
scale_y: 0,
opacity: 0,
@ -125,7 +125,7 @@ class DashItemContainer extends St.Widget {
this.label.ease({
opacity: 255,
duration: DASH_ITEM_LABEL_SHOW_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
@ -139,7 +139,7 @@ class DashItemContainer extends St.Widget {
opacity: 0,
duration: DASH_ITEM_LABEL_HIDE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this.label.hide()
onComplete: () => this.label.hide(),
});
}
@ -163,7 +163,7 @@ class DashItemContainer extends St.Widget {
scale_y: 1,
opacity: 255,
duration: time,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
@ -182,7 +182,7 @@ class DashItemContainer extends St.Widget {
opacity: 0,
duration: DASH_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this.destroy()
onComplete: () => this.destroy(),
});
}
});
@ -284,9 +284,12 @@ var DashActor = GObject.registerClass(
class DashActor extends St.Widget {
_init() {
let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL });
super._init({ name: 'dash',
layout_manager: layout,
clip_to_allocation: true });
super._init({
name: 'dash',
layout_manager: layout,
clip_to_allocation: true,
y_align: Clutter.ActorAlign.CENTER,
});
}
vfunc_allocate(box, flags) {
@ -330,8 +333,10 @@ class DashActor extends St.Widget {
const baseIconSizes = [16, 22, 24, 32, 48, 64];
var Dash = class Dash {
constructor() {
var Dash = GObject.registerClass({
Signals: { 'icon-size-changed': {} },
}, class Dash extends St.Bin {
_init() {
this._maxHeight = -1;
this.iconSize = 64;
this._shownInitially = false;
@ -359,11 +364,11 @@ var Dash = class Dash {
this._container.add_actor(this._showAppsIcon);
this.actor = new St.Bin({ child: this._container });
this.actor.connect('notify::height', () => {
if (this._maxHeight != this.actor.height)
super._init({ child: this._container });
this.connect('notify::height', () => {
if (this._maxHeight != this.height)
this._queueRedisplay();
this._maxHeight = this.actor.height;
this._maxHeight = this.height;
});
this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this));
@ -386,13 +391,13 @@ var Dash = class Dash {
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic');
Main.ctrlAltTabManager.addGroup(this, _("Dash"), 'user-bookmarks-symbolic');
}
_onDragBegin() {
this._dragCancelled = false;
this._dragMonitor = {
dragMotion: this._onDragMotion.bind(this)
dragMotion: this._onDragMotion.bind(this),
};
DND.addDragMonitor(this._dragMonitor);
@ -476,16 +481,16 @@ var Dash = class Dash {
let appIcon = new DashIcon(app);
appIcon.connect('menu-state-changed',
(appIcon, opened) => {
(o, opened) => {
this._itemMenuStateChanged(item, opened);
});
let item = new DashItemContainer();
item.setChild(appIcon.actor);
item.setChild(appIcon);
// Override default AppIcon label_actor, now the
// accessible_name is set at DashItemContainer.setLabelText
appIcon.actor.label_actor = null;
appIcon.label_actor = null;
item.setLabelText(app.get_name());
appIcon.icon.setIconSize(this.iconSize);
@ -624,7 +629,7 @@ var Dash = class Dash {
width: targetWidth,
height: targetHeight,
duration: DASH_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
}
@ -723,9 +728,10 @@ var Dash = class Dash {
}
}
for (let i = 0; i < addedItems.length; i++)
for (let i = 0; i < addedItems.length; i++) {
this._box.insert_child_at_index(addedItems[i].item,
addedItems[i].pos);
}
for (let i = 0; i < removedActors.length; i++) {
let item = removedActors[i];
@ -749,9 +755,8 @@ var Dash = class Dash {
if (!this._shownInitially)
this._shownInitially = true;
for (let i = 0; i < addedItems.length; i++) {
for (let i = 0; i < addedItems.length; i++)
addedItems[i].item.show(animate);
}
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
// Without it, StBoxLayout may use a stale size cache
@ -831,8 +836,8 @@ var Dash = class Dash {
}
this._dragPlaceholder = new DragPlaceholderItem();
this._dragPlaceholder.child.set_width (this.iconSize);
this._dragPlaceholder.child.set_height (this.iconSize / 2);
this._dragPlaceholder.child.set_width(this.iconSize);
this._dragPlaceholder.child.set_height(this.iconSize / 2);
this._box.insert_child_at_index(this._dragPlaceholder,
this._dragPlaceholderPos);
this._dragPlaceholder.show(fadeIn);
@ -846,7 +851,7 @@ var Dash = class Dash {
if (!this._dragPlaceholder)
return DND.DragMotionResult.NO_DROP;
let srcIsFavorite = (favPos != -1);
let srcIsFavorite = favPos != -1;
if (srcIsFavorite)
return DND.DragMotionResult.MOVE_DROP;
@ -859,9 +864,8 @@ var Dash = class Dash {
let app = getAppFromSource(source);
// Don't allow favoriting of transient apps
if (app == null || app.is_window_backed()) {
if (app == null || app.is_window_backed())
return false;
}
if (!global.settings.is_writable('favorite-apps'))
return false;
@ -870,7 +874,7 @@ var Dash = class Dash {
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let srcIsFavorite = (id in favorites);
let srcIsFavorite = id in favorites;
let favPos = 0;
let children = this._box.get_children();
@ -902,5 +906,4 @@ var Dash = class Dash {
return true;
}
};
Signals.addSignalMethods(Dash.prototype);
});

View File

@ -2,7 +2,7 @@
/* exported DateMenuButton */
const { Clutter, Gio, GLib, GnomeDesktop,
GObject, GWeather, Shell, St } = imports.gi;
GObject, GWeather, Pango, Shell, St } = imports.gi;
const Util = imports.misc.util;
const Main = imports.ui.main;
@ -25,24 +25,25 @@ function _isToday(date) {
now.getDate() == date.getDate();
}
var TodayButton = class TodayButton {
constructor(calendar) {
function _gDateTimeToDate(datetime) {
return new Date(datetime.to_unix() * 1000 + datetime.get_microsecond() / 1000);
}
var TodayButton = GObject.registerClass(
class TodayButton extends St.Button {
_init(calendar) {
// Having the ability to go to the current date if the user is already
// on the current date can be confusing. So don't make the button reactive
// until the selected date changes.
this.actor = new St.Button({
super._init({
style_class: 'datemenu-today-button',
x_align: St.Align.START,
x_expand: true,
can_focus: true,
reactive: false,
});
this.actor.connect('clicked', () => {
this._calendar.setDate(new Date(), false);
});
let hbox = new St.BoxLayout({ vertical: true });
this.actor.add_actor(hbox);
this.add_actor(hbox);
this._dayLabel = new St.Label({ style_class: 'day-label',
x_align: Clutter.ActorAlign.START });
@ -52,13 +53,17 @@ var TodayButton = class TodayButton {
hbox.add_actor(this._dateLabel);
this._calendar = calendar;
this._calendar.connect('selected-date-changed', (calendar, date) => {
this._calendar.connect('selected-date-changed', (_calendar, datetime) => {
// Make the button reactive only if the selected date is not the
// current date.
this.actor.reactive = !_isToday(date);
this.reactive = !_isToday(_gDateTimeToDate(datetime));
});
}
vfunc_clicked() {
this._calendar.setDate(new Date(), false);
}
setDate(date) {
this._dayLabel.set_text(date.toLocaleFormat('%A'));
@ -67,42 +72,38 @@ var TodayButton = class TodayButton {
* "Tue 9:29 AM"). The string itself should become a full date, e.g.,
* "February 17 2015".
*/
let dateFormat = Shell.util_translate_time_string (N_("%B %-d %Y"));
let dateFormat = Shell.util_translate_time_string(N_("%B %-d %Y"));
this._dateLabel.set_text(date.toLocaleFormat(dateFormat));
/* Translators: This is the accessible name of the date button shown
* below the time in the shell; it should combine the weekday and the
* date, e.g. "Tuesday February 17 2015".
*/
dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y"));
this.actor.accessible_name = date.toLocaleFormat(dateFormat);
dateFormat = Shell.util_translate_time_string(N_("%A %B %e %Y"));
this.accessible_name = date.toLocaleFormat(dateFormat);
}
};
});
var WorldClocksSection = class WorldClocksSection {
constructor() {
var WorldClocksSection = GObject.registerClass(
class WorldClocksSection extends St.Button {
_init() {
super._init({
style_class: 'world-clocks-button',
can_focus: true,
x_expand: true,
});
this._clock = new GnomeDesktop.WallClock();
this._clockNotifyId = 0;
this._locations = [];
this.actor = new St.Button({ style_class: 'world-clocks-button',
x_fill: true,
can_focus: true });
this.actor.connect('clicked', () => {
if (this._clocksApp)
this._clocksApp.activate();
Main.overview.hide();
Main.panel.closeCalendar();
});
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
this._grid = new St.Widget({ style_class: 'world-clocks-grid',
x_expand: true,
layout_manager: layout });
layout.hookup_style(this._grid);
this.actor.child = this._grid;
this.child = this._grid;
this._clocksApp = null;
this._clocksProxy = new ClocksProxy(
@ -114,7 +115,7 @@ var WorldClocksSection = class WorldClocksSection {
Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
this._settings = new Gio.Settings({
schema_id: 'org.gnome.shell.world-clocks'
schema_id: 'org.gnome.shell.world-clocks',
});
this._settings.connect('changed', this._clocksChanged.bind(this));
this._clocksChanged();
@ -125,9 +126,17 @@ var WorldClocksSection = class WorldClocksSection {
this._sync();
}
vfunc_clicked() {
if (this._clocksApp)
this._clocksApp.activate();
Main.overview.hide();
Main.panel.closeCalendar();
}
_sync() {
this._clocksApp = this._appSystem.lookup_app('org.gnome.clocks.desktop');
this.actor.visible = this._clocksApp != null;
this.visible = this._clocksApp != null;
}
_clocksChanged() {
@ -148,14 +157,14 @@ var WorldClocksSection = class WorldClocksSection {
});
let layout = this._grid.layout_manager;
let title = (this._locations.length == 0)
let title = this._locations.length == 0
? _("Add world clocks…")
: _("World Clocks");
let header = new St.Label({ style_class: 'world-clocks-header',
x_align: Clutter.ActorAlign.START,
text: title });
layout.attach(header, 0, 0, 2, 1);
this.actor.label_actor = header;
this.label_actor = header;
let localOffset = GLib.DateTime.new_now_local().get_utc_offset();
@ -173,8 +182,8 @@ var WorldClocksSection = class WorldClocksSection {
let otherOffset = this._getTimeAtLocation(l).get_utc_offset();
let offset = (otherOffset - localOffset) / GLib.TIME_SPAN_HOUR;
let fmt = (Math.trunc(offset) == offset) ? '%s%.0f' : '%s%.1f';
let prefix = (offset >= 0) ? '+' : '-';
let fmt = Math.trunc(offset) == offset ? '%s%.0f' : '%s%.1f';
let prefix = offset >= 0 ? '+' : '-';
let tz = new St.Label({ style_class: 'world-clocks-timezone',
text: fmt.format(prefix, Math.abs(offset)),
x_align: Clutter.ActorAlign.END,
@ -194,9 +203,10 @@ var WorldClocksSection = class WorldClocksSection {
}
if (this._grid.get_n_children() > 1) {
if (!this._clockNotifyId)
if (!this._clockNotifyId) {
this._clockNotifyId =
this._clock.connect('notify::clock', this._updateLabels.bind(this));
}
this._updateLabels();
} else {
if (this._clockNotifyId)
@ -236,45 +246,49 @@ var WorldClocksSection = class WorldClocksSection {
this._settings.set_value('locations',
new GLib.Variant('av', this._clocksProxy.Locations));
}
};
});
var WeatherSection = GObject.registerClass(
class WeatherSection extends St.Button {
_init() {
super._init({
style_class: 'weather-button',
can_focus: true,
x_expand: true,
});
var WeatherSection = class WeatherSection {
constructor() {
this._weatherClient = new Weather.WeatherClient();
this.actor = new St.Button({ style_class: 'weather-button',
x_fill: true,
can_focus: true });
this.actor.connect('clicked', () => {
this._weatherClient.activateApp();
Main.overview.hide();
Main.panel.closeCalendar();
});
this.actor.connect('notify::mapped', () => {
if (this.actor.mapped)
this._weatherClient.update();
let box = new St.BoxLayout({
style_class: 'weather-box',
vertical: true,
x_expand: true,
});
let box = new St.BoxLayout({ style_class: 'weather-box',
vertical: true });
this.child = box;
this.actor.child = box;
let titleBox = new St.BoxLayout();
titleBox.add_child(new St.Label({ style_class: 'weather-header',
x_align: Clutter.ActorAlign.START,
x_expand: true,
text: _("Weather") }));
let titleBox = new St.BoxLayout({ style_class: 'weather-header-box' });
titleBox.add_child(new St.Label({
style_class: 'weather-header',
x_align: Clutter.ActorAlign.START,
x_expand: true,
y_align: Clutter.ActorAlign.END,
text: _('Weather'),
}));
box.add_child(titleBox);
this._titleLocation = new St.Label({ style_class: 'weather-header location',
x_align: Clutter.ActorAlign.END });
this._titleLocation = new St.Label({
style_class: 'weather-header location',
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
});
titleBox.add_child(this._titleLocation);
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
this._forecastGrid = new St.Widget({ style_class: 'weather-grid',
layout_manager: layout });
this._forecastGrid = new St.Widget({
style_class: 'weather-grid',
layout_manager: layout,
});
layout.hookup_style(this._forecastGrid);
box.add_child(this._forecastGrid);
@ -282,26 +296,40 @@ var WeatherSection = class WeatherSection {
this._sync();
}
vfunc_map() {
this._weatherClient.update();
super.vfunc_map();
}
vfunc_clicked() {
this._weatherClient.activateApp();
Main.overview.hide();
Main.panel.closeCalendar();
}
_getInfos() {
let info = this._weatherClient.info;
let forecasts = info.get_forecast_list();
let forecasts = this._weatherClient.info.get_forecast_list();
let current = info;
let infos = [info];
let now = GLib.DateTime.new_now_local();
let current = GLib.DateTime.new_from_unix_local(0);
let infos = [];
for (let i = 0; i < forecasts.length; i++) {
let [ok_, timestamp] = forecasts[i].get_value_update();
let datetime = new Date(timestamp * 1000);
if (!_isToday(datetime))
continue; // Ignore forecasts from other days
const [valid, timestamp] = forecasts[i].get_value_update();
if (!valid || timestamp === 0)
continue; // 0 means 'never updated'
[ok_, timestamp] = current.get_value_update();
let currenttime = new Date(timestamp * 1000);
if (currenttime.getHours() == datetime.getHours())
const datetime = GLib.DateTime.new_from_unix_local(timestamp);
if (now.difference(datetime) > 0)
continue; // Ignore earlier forecasts
if (datetime.difference(current) < GLib.TIME_SPAN_HOUR)
continue; // Enforce a minimum interval of 1h
current = forecasts[i];
if (infos.push(current) == MAX_FORECASTS)
if (infos.push(forecasts[i]) == MAX_FORECASTS)
break; // Use a maximum of five forecasts
current = datetime;
}
return infos;
}
@ -315,21 +343,31 @@ var WeatherSection = class WeatherSection {
let col = 0;
infos.forEach(fc => {
let [ok_, timestamp] = fc.get_value_update();
const [valid_, timestamp] = fc.get_value_update();
let timeStr = Util.formatTime(new Date(timestamp * 1000), {
timeOnly: true
timeOnly: true,
ampm: false,
});
let icon = new St.Icon({ style_class: 'weather-forecast-icon',
icon_name: fc.get_symbolic_icon_name(),
x_align: Clutter.ActorAlign.CENTER,
x_expand: true });
let temp = new St.Label({ style_class: 'weather-forecast-temp',
text: fc.get_temp_summary(),
x_align: Clutter.ActorAlign.CENTER });
let time = new St.Label({ style_class: 'weather-forecast-time',
text: timeStr,
x_align: Clutter.ActorAlign.CENTER });
let icon = new St.Icon({
style_class: 'weather-forecast-icon',
icon_name: fc.get_symbolic_icon_name(),
x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
});
let temp = new St.Label({
style_class: 'weather-forecast-temp',
text: fc.get_temp_summary(),
x_align: Clutter.ActorAlign.CENTER,
});
let time = new St.Label({
style_class: 'weather-forecast-time',
text: timeStr,
x_align: Clutter.ActorAlign.CENTER,
});
temp.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
time.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
layout.attach(icon, col, 0, 1, 1);
layout.attach(temp, col, 1, 1, 1);
@ -353,7 +391,12 @@ var WeatherSection = class WeatherSection {
}
let info = this._weatherClient.info;
this._titleLocation.text = info.get_location().get_name();
let loc = info.get_location();
if (loc.get_level() !== GWeather.LocationLevel.CITY && loc.has_coords()) {
let world = GWeather.Location.get_world();
loc = world.find_nearest_city(...loc.get_coords());
}
this._titleLocation.text = loc.get_name();
if (this._weatherClient.loading) {
this._setStatusLabel(_("Loading…"));
@ -372,23 +415,27 @@ var WeatherSection = class WeatherSection {
}
_sync() {
this.actor.visible = this._weatherClient.available;
this.visible = this._weatherClient.available;
if (!this.actor.visible)
if (!this.visible)
return;
this._titleLocation.visible = this._weatherClient.hasLocation;
this._updateForecasts();
}
};
});
var MessagesIndicator = class MessagesIndicator {
constructor() {
this.actor = new St.Icon({ icon_name: 'message-indicator-symbolic',
icon_size: 16,
visible: false, y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
var MessagesIndicator = GObject.registerClass(
class MessagesIndicator extends St.Icon {
_init() {
super._init({
icon_name: 'message-indicator-symbolic',
icon_size: 16,
visible: false,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER,
});
this._sources = [];
@ -401,7 +448,7 @@ var MessagesIndicator = class MessagesIndicator {
}
_onSourceAdded(tray, source) {
source.connect('count-updated', this._updateCount.bind(this));
source.connect('notify::count', this._updateCount.bind(this));
this._sources.push(source);
this._updateCount();
}
@ -416,9 +463,9 @@ var MessagesIndicator = class MessagesIndicator {
this._sources.forEach(source => (count += source.unseenCount));
count -= Main.messageTray.queueCount;
this.actor.visible = (count > 0);
this.visible = count > 0;
}
};
});
var IndicatorPad = GObject.registerClass(
class IndicatorPad extends St.Widget {
@ -511,13 +558,13 @@ class DateMenuButton extends PanelMenu.Button {
this._indicator = new MessagesIndicator();
let box = new St.BoxLayout();
box.add_actor(new IndicatorPad(this._indicator.actor));
box.add_actor(new IndicatorPad(this._indicator));
box.add_actor(this._clockDisplay);
box.add_actor(this._indicator.actor);
box.add_actor(this._indicator);
this.label_actor = this._clockDisplay;
this.add_actor(box);
this.add_style_class_name ('clock-display');
this.add_style_class_name('clock-display');
let layout = new FreezableBinLayout();
let bin = new St.Widget({ layout_manager: layout });
@ -529,11 +576,11 @@ class DateMenuButton extends PanelMenu.Button {
bin.add_actor(hbox);
this._calendar = new Calendar.Calendar();
this._calendar.connect('selected-date-changed',
(calendar, date) => {
layout.frozen = !_isToday(date);
this._messageList.setDate(date);
});
this._calendar.connect('selected-date-changed', (_calendar, datetime) => {
let date = _gDateTimeToDate(datetime);
layout.frozen = !_isToday(date);
this._messageList.setDate(date);
});
this.menu.connect('open-state-changed', (menu, isOpen) => {
// Whenever the menu is opened, select today
@ -547,35 +594,36 @@ class DateMenuButton extends PanelMenu.Button {
// Fill up the first column
this._messageList = new Calendar.CalendarMessageList();
hbox.add(this._messageList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
hbox.add_child(this._messageList);
// Fill up the second column
let boxLayout = new CalendarColumnLayout(this._calendar.actor);
let boxLayout = new CalendarColumnLayout(this._calendar);
vbox = new St.Widget({ style_class: 'datemenu-calendar-column',
layout_manager: boxLayout });
boxLayout.hookup_style(vbox);
hbox.add(vbox);
this._date = new TodayButton(this._calendar);
vbox.add_actor(this._date.actor);
vbox.add_actor(this._date);
vbox.add_actor(this._calendar.actor);
vbox.add_actor(this._calendar);
this._displaysSection = new St.ScrollView({ style_class: 'datemenu-displays-section vfade',
x_expand: true, x_fill: true,
x_expand: true,
overlay_scrollbars: true });
this._displaysSection.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
vbox.add_actor(this._displaysSection);
let displaysBox = new St.BoxLayout({ vertical: true,
x_expand: true,
style_class: 'datemenu-displays-box' });
this._displaysSection.add_actor(displaysBox);
this._clocksItem = new WorldClocksSection();
displaysBox.add(this._clocksItem.actor, { x_fill: true });
displaysBox.add_child(this._clocksItem);
this._weatherItem = new WeatherSection();
displaysBox.add(this._weatherItem.actor, { x_fill: true });
displaysBox.add_child(this._weatherItem);
// Done with hbox for calendar and event list
@ -613,11 +661,11 @@ class DateMenuButton extends PanelMenu.Button {
_sessionUpdated() {
let eventSource;
let showEvents = Main.sessionMode.showCalendarEvents;
if (showEvents) {
if (showEvents)
eventSource = this._getEventSource();
} else {
else
eventSource = new Calendar.EmptyEventSource();
}
this._setEventSource(eventSource);
// Displays are not actually expected to launch Settings when activated

View File

@ -36,19 +36,15 @@ class Dialog extends St.Widget {
this._dialog.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
this._dialog.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
this.contentLayout = new St.BoxLayout({ vertical: true,
style_class: "modal-dialog-content-box" });
this._dialog.add(this.contentLayout,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.START });
this.contentLayout = new St.BoxLayout({
vertical: true,
style_class: 'modal-dialog-content-box',
y_expand: true,
});
this._dialog.add_child(this.contentLayout);
this.buttonLayout = new St.Widget ({ layout_manager: new Clutter.BoxLayout({ homogeneous: true }) });
this._dialog.add(this.buttonLayout,
{ x_align: St.Align.MIDDLE,
y_align: St.Align.START });
this.buttonLayout = new St.Widget({ layout_manager: new Clutter.BoxLayout({ homogeneous: true }) });
this._dialog.add_child(this.buttonLayout);
}
_onDestroy() {
@ -100,7 +96,7 @@ class Dialog extends St.Widget {
}
addContent(actor) {
this.contentLayout.add (actor, { expand: true });
this.contentLayout.add(actor, { expand: true });
}
addButton(buttonInfo) {
@ -121,7 +117,7 @@ class Dialog extends St.Widget {
can_focus: true,
x_expand: true,
y_expand: true,
label: label });
label });
button.connect('clicked', action);
buttonInfo['button'] = button;
@ -163,8 +159,8 @@ var MessageDialogContent = GObject.registerClass({
'body': GObject.ParamSpec.string('body', 'body', 'body',
GObject.ParamFlags.READWRITE |
GObject.ParamFlags.CONSTRUCT,
null)
}
null),
},
}, class MessageDialogContent extends St.BoxLayout {
_init(params) {
this._icon = new St.Icon({ y_align: Clutter.ActorAlign.START });
@ -215,7 +211,7 @@ var MessageDialogContent = GObject.registerClass({
set icon(icon) {
this._icon.set({
gicon: icon,
visible: icon != null
visible: icon != null,
});
this.notify('icon');
}
@ -235,7 +231,7 @@ var MessageDialogContent = GObject.registerClass({
_setLabel(label, prop, value) {
label.set({
text: value || '',
visible: value != null
visible: value != null,
});
this.notify(prop);
}

View File

@ -18,7 +18,7 @@ var DragMotionResult = {
NO_DROP: 0,
COPY_DROP: 1,
MOVE_DROP: 2,
CONTINUE: 3
CONTINUE: 3,
};
var DragState = {
@ -30,13 +30,13 @@ var DragState = {
var DRAG_CURSOR_MAP = {
0: Meta.Cursor.DND_UNSUPPORTED_TARGET,
1: Meta.Cursor.DND_COPY,
2: Meta.Cursor.DND_MOVE
2: Meta.Cursor.DND_MOVE,
};
var DragDropResult = {
FAILURE: 0,
SUCCESS: 1,
CONTINUE: 2
CONTINUE: 2,
};
var dragMonitors = [];
@ -61,11 +61,12 @@ function addDragMonitor(monitor) {
}
function removeDragMonitor(monitor) {
for (let i = 0; i < dragMonitors.length; i++)
for (let i = 0; i < dragMonitors.length; i++) {
if (dragMonitors[i] == monitor) {
dragMonitors.splice(i, 1);
return;
}
}
}
var _Draggable = class _Draggable {
@ -150,12 +151,12 @@ var _Draggable = class _Draggable {
if (touchSequence)
pointer.sequence_grab(touchSequence, actor);
else if (pointer)
pointer.grab (actor);
pointer.grab(actor);
this._grabbedDevice = pointer;
this._touchSequence = touchSequence;
this._capturedEventId = global.stage.connect('captured-event', (actor, event) => {
this._capturedEventId = global.stage.connect('captured-event', (o, event) => {
let device = event.get_device();
if (device != this._grabbedDevice &&
device.get_device_type() != Clutter.InputDeviceType.KEYBOARD_DEVICE)
@ -171,7 +172,7 @@ var _Draggable = class _Draggable {
}
if (this._touchSequence)
this._grabbedDevice.sequence_ungrab (this._touchSequence);
this._grabbedDevice.sequence_ungrab(this._touchSequence);
else
this._grabbedDevice.ungrab();
@ -212,9 +213,9 @@ var _Draggable = class _Draggable {
_eventIsRelease(event) {
if (event.type() == Clutter.EventType.BUTTON_RELEASE) {
let buttonMask = (Clutter.ModifierType.BUTTON1_MASK |
let buttonMask = Clutter.ModifierType.BUTTON1_MASK |
Clutter.ModifierType.BUTTON2_MASK |
Clutter.ModifierType.BUTTON3_MASK);
Clutter.ModifierType.BUTTON3_MASK;
/* We only obey the last button release from the device,
* other buttons may get pressed/released during the DnD op.
*/
@ -258,16 +259,16 @@ var _Draggable = class _Draggable {
} else if (event.type() == Clutter.EventType.MOTION ||
(event.type() == Clutter.EventType.TOUCH_UPDATE &&
global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) {
if (this._dragActor && this._dragState == DragState.DRAGGING) {
if (this._dragActor && this._dragState == DragState.DRAGGING)
return this._updateDragPosition(event);
} else if (this._dragActor == null && this._dragState != DragState.CANCELLED) {
else if (this._dragActor == null && this._dragState != DragState.CANCELLED)
return this._maybeStartDrag(event);
}
// We intercept KEY_PRESS event so that we can process Esc key press to cancel
// dragging and ignore all other key presses.
} else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragState == DragState.DRAGGING) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
if (symbol == Clutter.KEY_Escape) {
this._cancelDrag(event.get_time());
return Clutter.EVENT_STOP;
}
@ -291,9 +292,11 @@ var _Draggable = class _Draggable {
/**
* startDrag:
* @stageX: X coordinate of event
* @stageY: Y coordinate of event
* @time: Event timestamp
* @param {number} stageX: X coordinate of event
* @param {number} stageY: Y coordinate of event
* @param {number} time: Event timestamp
* @param {Clutter.EventSequence=} sequence: Event sequence
* @param {Clutter.InputDevice=} device: device that originated the event
*
* Directly initiate a drag and drop operation from the given actor.
* This function is useful to call if you've specified manualMode
@ -338,7 +341,7 @@ var _Draggable = class _Draggable {
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor();
Main.uiGroup.add_child(this._dragActor);
this._dragActor.raise_top();
Main.uiGroup.set_child_above_sibling(this._dragActor, null);
Shell.util_set_hidden_from_pick(this._dragActor, true);
// Drag actor does not always have to be the same as actor. For example drag actor
@ -388,7 +391,7 @@ var _Draggable = class _Draggable {
this._dragOrigParent.remove_actor(this._dragActor);
Main.uiGroup.add_child(this._dragActor);
this._dragActor.raise_top();
Main.uiGroup.set_child_above_sibling(this._dragActor, null);
Shell.util_set_hidden_from_pick(this._dragActor, true);
}
@ -427,7 +430,7 @@ var _Draggable = class _Draggable {
scale_x: scale * origScale,
scale_y: scale * origScale,
duration: SCALE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
this._dragActor.get_transition('scale-x').connect('new-frame', () => {
@ -472,7 +475,7 @@ var _Draggable = class _Draggable {
y: this._dragY,
dragActor: this._dragActor,
source: this.actor._delegate,
targetActor: target
targetActor: target,
};
let targetActorDestroyHandlerId;
@ -551,11 +554,11 @@ var _Draggable = class _Draggable {
let dropEvent = {
dropActor: this._dragActor,
targetActor: target,
clutterEvent: event
clutterEvent: event,
};
for (let i = 0; i < dragMonitors.length; i++) {
let dropFunc = dragMonitors[i].dragDrop;
if (dropFunc)
if (dropFunc) {
switch (dropFunc(dropEvent)) {
case DragDropResult.FAILURE:
case DragDropResult.SUCCESS:
@ -563,6 +566,7 @@ var _Draggable = class _Draggable {
case DragDropResult.CONTINUE:
continue;
}
}
}
// At this point it is too late to cancel a drag by destroying
@ -573,11 +577,15 @@ var _Draggable = class _Draggable {
while (target) {
if (target._delegate && target._delegate.acceptDrop) {
let [r_, targX, targY] = target.transform_stage_point(dropX, dropY);
if (target._delegate.acceptDrop(this.actor._delegate,
this._dragActor,
targX,
targY,
event.get_time())) {
let accepted = false;
try {
accepted = target._delegate.acceptDrop(this.actor._delegate,
this._dragActor, targX, targY, event.get_time());
} catch (e) {
// On error, skip this target
logError(e, "Skipping drag target");
}
if (accepted) {
// If it accepted the drop without taking the actor,
// handle it ourselves.
if (this._dragActor && this._dragActor.get_parent() == Main.uiGroup) {
@ -638,7 +646,7 @@ var _Draggable = class _Draggable {
_cancelDrag(eventTime) {
this.emit('drag-cancelled', eventTime);
let wasCancelled = (this._dragState == DragState.CANCELLED);
let wasCancelled = this._dragState == DragState.CANCELLED;
this._dragState = DragState.CANCELLED;
if (this._actorDestroyed || wasCancelled) {
@ -659,7 +667,7 @@ var _Draggable = class _Draggable {
y: snapBackY,
scale_x: snapBackScale,
scale_y: snapBackScale,
duration: SNAP_BACK_ANIMATION_TIME
duration: SNAP_BACK_ANIMATION_TIME,
});
}
@ -673,7 +681,7 @@ var _Draggable = class _Draggable {
this._dragActor.opacity = 0;
this._animateDragEnd(eventTime, {
duration: REVERT_ANIMATION_TIME
duration: REVERT_ANIMATION_TIME,
});
}
@ -686,7 +694,7 @@ var _Draggable = class _Draggable {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._onAnimationComplete(this._dragActor, eventTime);
}
},
}));
}
@ -740,8 +748,9 @@ Signals.addSignalMethods(_Draggable.prototype);
/**
* makeDraggable:
* @actor: Source actor
* @params: (optional) Additional parameters
* @param {Clutter.Actor} actor: Source actor
* @param {Object=} params: Additional parameters
* @returns {Object} a new Draggable
*
* Create an object which controls drag and drop for the given actor.
*

View File

@ -37,17 +37,17 @@ var EdgeDragAction = GObject.registerClass({
let [x, y] = this.get_press_coords(0);
let monitorRect = this._getMonitorRect(x, y);
return ((this._side == St.Side.LEFT && x < monitorRect.x + EDGE_THRESHOLD) ||
return (this._side == St.Side.LEFT && x < monitorRect.x + EDGE_THRESHOLD) ||
(this._side == St.Side.RIGHT && x > monitorRect.x + monitorRect.width - EDGE_THRESHOLD) ||
(this._side == St.Side.TOP && y < monitorRect.y + EDGE_THRESHOLD) ||
(this._side == St.Side.BOTTOM && y > monitorRect.y + monitorRect.height - EDGE_THRESHOLD));
(this._side == St.Side.BOTTOM && y > monitorRect.y + monitorRect.height - EDGE_THRESHOLD);
}
vfunc_gesture_progress(_actor) {
let [startX, startY] = this.get_press_coords(0);
let [x, y] = this.get_motion_coords(0);
let offsetX = Math.abs (x - startX);
let offsetY = Math.abs (y - startY);
let offsetX = Math.abs(x - startX);
let offsetY = Math.abs(y - startY);
if (offsetX < EDGE_THRESHOLD && offsetY < EDGE_THRESHOLD)
return true;

View File

@ -128,7 +128,7 @@ const DialogType = {
SHUTDOWN: 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */,
RESTART: 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */,
UPDATE_RESTART: 3,
UPGRADE_RESTART: 4
UPGRADE_RESTART: 4,
};
const DialogContent = {
@ -136,7 +136,7 @@ const DialogContent = {
1 /* DialogType.SHUTDOWN */: shutdownDialogContent,
2 /* DialogType.RESTART */: restartDialogContent,
3 /* DialogType.UPDATE_RESTART */: restartUpdateDialogContent,
4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent
4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent,
};
var MAX_USERS_IN_SESSION_DIALOG = 5;
@ -207,10 +207,10 @@ function _setCheckBoxLabel(checkBox, text) {
if (text) {
label.set_text(text);
checkBox.actor.show();
checkBox.show();
} else {
label.set_text('');
checkBox.actor.hide();
checkBox.hide();
}
}
@ -218,7 +218,7 @@ function init() {
// This always returns the same singleton object
// By instantiating it initially, we register the
// bus object, etc.
(new EndSessionDialog());
new EndSessionDialog();
}
var EndSessionDialog = GObject.registerClass(
@ -263,42 +263,39 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._userLoadedId = this._user.connect('notify::is_loaded', this._sync.bind(this));
this._userChangedId = this._user.connect('changed', this._sync.bind(this));
let mainContentLayout = new St.BoxLayout({ vertical: false });
this.contentLayout.add(mainContentLayout,
{ x_fill: true,
y_fill: false });
let mainContentLayout = new St.BoxLayout({
vertical: false,
x_expand: true,
y_expand: false,
});
this.contentLayout.add_child(mainContentLayout);
this._iconBin = new St.Bin();
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
this._iconBin = new St.Bin({
x_expand: true,
x_align: Clutter.ActorAlign.END,
});
mainContentLayout.add_child(this._iconBin);
let messageLayout = new St.BoxLayout({ vertical: true,
style_class: 'end-session-dialog-layout' });
mainContentLayout.add(messageLayout,
{ y_align: St.Align.START });
mainContentLayout.add_child(messageLayout);
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
messageLayout.add(this._subjectLabel,
{ x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
messageLayout.add_child(this._subjectLabel);
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
this._descriptionLabel = new St.Label({
style_class: 'end-session-dialog-description',
y_expand: true,
});
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._descriptionLabel.clutter_text.line_wrap = true;
messageLayout.add(this._descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
messageLayout.add_child(this._descriptionLabel);
this._checkBox = new CheckBox.CheckBox();
this._checkBox.actor.connect('clicked', this._sync.bind(this));
messageLayout.add(this._checkBox.actor);
this._checkBox.connect('clicked', this._sync.bind(this));
messageLayout.add(this._checkBox);
this._batteryWarning = new St.Label({ style_class: 'end-session-dialog-warning',
text: _("Running on battery power: please plug in before installing updates.") });
@ -306,11 +303,13 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._batteryWarning.clutter_text.line_wrap = true;
messageLayout.add(this._batteryWarning);
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
this._scrollView = new St.ScrollView({
style_class: 'end-session-dialog-list',
x_expand: true,
y_expand: true,
});
this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
this.contentLayout.add(this._scrollView,
{ x_fill: true,
y_fill: true });
this.contentLayout.add_child(this._scrollView);
this._scrollView.hide();
this._inhibitorSection = new St.BoxLayout({ vertical: true,
@ -367,7 +366,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
}
_sync() {
let open = (this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED);
let open = this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED;
if (!open)
return;
@ -376,12 +375,12 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
let subject = dialogContent.subject;
// Use different title when we are installing updates
if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
if (dialogContent.subjectWithUpdates && this._checkBox.checked)
subject = dialogContent.subjectWithUpdates;
if (dialogContent.showBatteryWarning) {
// Warn when running on battery power
if (this._powerProxy.OnBattery && this._checkBox.actor.checked)
if (this._powerProxy.OnBattery && this._checkBox.checked)
this._batteryWarning.opacity = 255;
else
this._batteryWarning.opacity = 0;
@ -429,7 +428,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
let avatarWidget = new UserWidget.Avatar(this._user,
{ iconSize: _DIALOG_ICON_SIZE,
styleClass: dialogContent.iconStyleClass });
this._iconBin.child = avatarWidget.actor;
this._iconBin.child = avatarWidget;
avatarWidget.update();
}
@ -444,7 +443,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
let dialogContent = DialogContent[this._type];
let buttons = [{ action: this.cancel.bind(this),
label: _("Cancel"),
key: Clutter.Escape }];
key: Clutter.KEY_Escape }];
for (let i = 0; i < dialogContent.confirmButtons.length; i++) {
let signal = dialogContent.confirmButtons[i].signal;
@ -457,7 +456,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._confirm(signal);
});
},
label: label,
label,
});
}
@ -485,13 +484,13 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
};
// Offline update not available; just emit the signal
if (!this._checkBox.actor.visible) {
if (!this._checkBox.visible) {
callback();
return;
}
// Trigger the offline update as requested
if (this._checkBox.actor.checked) {
if (this._checkBox.checked) {
switch (signal) {
case "ConfirmedReboot":
this._triggerOfflineUpdateReboot(callback);
@ -567,7 +566,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
this._timerId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
let currentTime = GLib.get_monotonic_time();
let secondsElapsed = ((currentTime - startTime) / 1000000);
let secondsElapsed = (currentTime - startTime) / 1000000;
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
if (this._secondsLeft > 0) {
@ -656,7 +655,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
let actor = new St.BoxLayout({ style_class: 'end-session-dialog-session-list-item',
can_focus: true });
actor.add(avatar.actor);
actor.add(avatar);
let nameLabel = new St.Label({ text: userLabelText,
style_class: 'end-session-dialog-session-list-item-name',
@ -682,11 +681,12 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
continue;
let sessionId = GLib.getenv('XDG_SESSION_ID');
if (!sessionId)
if (!sessionId) {
this._loginManager.getCurrentSessionProxy(currentSessionProxy => {
sessionId = currentSessionProxy.Id;
log(`endSessionDialog: No XDG_SESSION_ID, fetched from logind: ${sessionId}`);
});
}
if (proxy.Id == sessionId)
continue;
@ -754,14 +754,14 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || '');
this._checkBox.actor.visible = (dialogContent.checkBoxText && updatePrepared && updatesAllowed);
this._checkBox.actor.checked = (updatePrepared && updateTriggered);
this._checkBox.visible = dialogContent.checkBoxText && updatePrepared && updatesAllowed;
this._checkBox.checked = updatePrepared && updateTriggered;
// We show the warning either together with the checkbox, or when
// updates have already been triggered, but the user doesn't have
// enough permissions to cancel them.
this._batteryWarning.visible = (dialogContent.showBatteryWarning &&
(this._checkBox.actor.visible || updatePrepared && updateTriggered && !updatesAllowed));
this._batteryWarning.visible = dialogContent.showBatteryWarning &&
(this._checkBox.visible || updatePrepared && updateTriggered && !updatesAllowed);
this._updateButtons();

View File

@ -10,7 +10,7 @@ imports.gi.versions.Gtk = '3.0';
imports.gi.versions.TelepathyGLib = '0.12';
imports.gi.versions.TelepathyLogger = '0.2';
const { Clutter, GLib, Meta, Shell, St } = imports.gi;
const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
const Gettext = imports.gettext;
// We can't import shell JS modules yet, because they may have
@ -23,7 +23,7 @@ const Gettext = imports.gettext;
// of interfaces in Javascript
function _patchContainerClass(containerClass) {
// This one is a straightforward mapping of the C method
containerClass.prototype.child_set = function(actor, props) {
containerClass.prototype.child_set = function (actor, props) {
let meta = this.get_child_meta(actor);
for (let prop in props)
meta[prop] = props[prop];
@ -32,7 +32,7 @@ function _patchContainerClass(containerClass) {
// clutter_container_add() actually is a an add-many-actors
// method. We conveniently, but somewhat dubiously, take the
// this opportunity to make it do something more useful.
containerClass.prototype.add = function(actor, props) {
containerClass.prototype.add = function (actor, props) {
this.add_actor(actor);
if (props)
this.child_set(actor, props);
@ -40,8 +40,8 @@ function _patchContainerClass(containerClass) {
}
function _patchLayoutClass(layoutClass, styleProps) {
if (styleProps)
layoutClass.prototype.hookup_style = function(container) {
if (styleProps) {
layoutClass.prototype.hookup_style = function (container) {
container.connect('style-changed', () => {
let node = container.get_theme_node();
for (let prop in styleProps) {
@ -51,11 +51,7 @@ function _patchLayoutClass(layoutClass, styleProps) {
}
});
};
layoutClass.prototype.child_set = function(actor, props) {
let meta = this.get_child_meta(actor.get_parent(), actor);
for (let prop in props)
meta[prop] = props[prop];
};
}
}
function _makeEaseCallback(params, cleanup) {
@ -105,12 +101,20 @@ function _easeActor(actor, params) {
actor.set_easing_delay(params.delay);
delete params.delay;
let repeatCount = 0;
if (params.repeatCount != undefined)
repeatCount = params.repeatCount;
delete params.repeatCount;
let autoReverse = false;
if (params.autoReverse != undefined)
autoReverse = params.autoReverse;
delete params.autoReverse;
if (params.mode != undefined)
actor.set_easing_mode(params.mode);
delete params.mode;
Meta.disable_unredirect_for_display(global.display);
let cleanup = () => Meta.enable_unredirect_for_display(global.display);
let callback = _makeEaseCallback(params, cleanup);
@ -124,10 +128,17 @@ function _easeActor(actor, params) {
let transition = animatedProps.map(p => actor.get_transition(p))
.find(t => t !== null);
if (transition)
transition.connect('stopped', (t, finished) => callback(finished));
if (transition && transition.delay)
transition.connect('started', () => Meta.disable_unredirect_for_display(global.display));
else
Meta.disable_unredirect_for_display(global.display);
if (transition) {
transition.set({ repeatCount, autoReverse });
transition.connect('stopped', (t, finished) => callback(finished));
} else {
callback(true);
}
}
function _easeActorProperty(actor, propName, target, params) {
@ -140,13 +151,21 @@ function _easeActorProperty(actor, propName, target, params) {
params.duration = adjustAnimationTime(params.duration);
let duration = Math.floor(params.duration || 0);
let repeatCount = 0;
if (params.repeatCount != undefined)
repeatCount = params.repeatCount;
delete params.repeatCount;
let autoReverse = false;
if (params.autoReverse != undefined)
autoReverse = params.autoReverse;
delete params.autoReverse;
// Copy Clutter's behavior for implicit animations, see
// should_skip_implicit_transition()
if (actor instanceof Clutter.Actor && !actor.mapped)
duration = 0;
Meta.disable_unredirect_for_display(global.display);
let cleanup = () => Meta.enable_unredirect_for_display(global.display);
let callback = _makeEaseCallback(params, cleanup);
@ -157,6 +176,7 @@ function _easeActorProperty(actor, propName, target, params) {
let [obj, prop] = _getPropertyTarget(actor, propName);
obj[prop] = target;
Meta.disable_unredirect_for_display(global.display);
callback(true);
return;
@ -166,12 +186,19 @@ function _easeActorProperty(actor, propName, target, params) {
let transition = new Clutter.PropertyTransition(Object.assign({
property_name: propName,
interval: new Clutter.Interval({ value_type: pspec.value_type }),
remove_on_complete: true
remove_on_complete: true,
repeat_count: repeatCount,
auto_reverse: autoReverse,
}, params));
actor.add_transition(propName, transition);
transition.set_to(target);
if (transition.delay)
transition.connect('started', () => Meta.disable_unredirect_for_display(global.display));
else
Meta.disable_unredirect_for_display(global.display);
transition.connect('stopped', (t, finished) => callback(finished));
}
@ -202,37 +229,37 @@ function init() {
window.ngettext = Gettext.ngettext;
window.N_ = s => s;
GObject.gtypeNameBasedOnJSPath = true;
// Miscellaneous monkeypatching
_patchContainerClass(St.BoxLayout);
_patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns' });
_patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns' });
_patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
let origSetEasingDuration = Clutter.Actor.prototype.set_easing_duration;
Clutter.Actor.prototype.set_easing_duration = function(msecs) {
Clutter.Actor.prototype.set_easing_duration = function (msecs) {
origSetEasingDuration.call(this, adjustAnimationTime(msecs));
};
let origSetEasingDelay = Clutter.Actor.prototype.set_easing_delay;
Clutter.Actor.prototype.set_easing_delay = function(msecs) {
Clutter.Actor.prototype.set_easing_delay = function (msecs) {
origSetEasingDelay.call(this, adjustAnimationTime(msecs));
};
Clutter.Actor.prototype.ease = function(props, easingParams) {
Clutter.Actor.prototype.ease = function (props, easingParams) {
_easeActor(this, props, easingParams);
};
Clutter.Actor.prototype.ease_property = function(propName, target, params) {
Clutter.Actor.prototype.ease_property = function (propName, target, params) {
_easeActorProperty(this, propName, target, params);
};
St.Adjustment.prototype.ease = function(target, params) {
St.Adjustment.prototype.ease = function (target, params) {
// we're not an actor of course, but we implement the same
// transition API as Clutter.Actor, so this works anyway
_easeActorProperty(this, 'value', target, params);
};
Clutter.Actor.prototype.toString = function() {
Clutter.Actor.prototype.toString = function () {
return St.describe_actor(this);
};
// Deprecation warning for former JS classes turned into an actor subclass
@ -242,17 +269,17 @@ function init() {
let { stack } = new Error();
log(`Usage of object.actor is deprecated for ${klass}\n${stack}`);
return this;
}
},
});
St.set_slow_down_factor = function(factor) {
St.set_slow_down_factor = function (factor) {
let { stack } = new Error();
log(`St.set_slow_down_factor() is deprecated, use St.Settings.slow_down_factor\n${stack}`);
St.Settings.get().slow_down_factor = factor;
};
let origToString = Object.prototype.toString;
Object.prototype.toString = function() {
Object.prototype.toString = function () {
let base = origToString.call(this);
try {
if ('actor' in this && this.actor instanceof Clutter.Actor)
@ -265,8 +292,9 @@ function init() {
};
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
Date.prototype.toLocaleFormat = function(format) {
return Shell.util_format_date(format, this.getTime());
Date.prototype.toLocaleFormat = function (format) {
let dt = GLib.DateTime.new_from_unix_local(this.getTime() / 1000);
return dt ? dt.format(format) : '';
};
let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');

View File

@ -19,12 +19,12 @@ var REPOSITORY_URL_UPDATE = `${REPOSITORY_URL_BASE}/update-info/`;
let _httpSession;
function installExtension(uuid, invocation) {
let params = { uuid: uuid,
let params = { uuid,
shell_version: Config.PACKAGE_VERSION };
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
_httpSession.queue_message(message, (session, message) => {
_httpSession.queue_message(message, () => {
if (message.status_code != Soup.KnownStatusCode.OK) {
Main.extensionManager.logExtensionError(uuid, `downloading info: ${message.status_code}`);
invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString());
@ -90,7 +90,7 @@ function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, (pid, status) => {
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, (o, status) => {
GLib.spawn_close_pid(pid);
if (status != 0)
@ -113,7 +113,7 @@ function updateExtension(uuid) {
let url = REPOSITORY_URL_DOWNLOAD.format(uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message, (session, message) => {
_httpSession.queue_message(message, session => {
gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, () => {
let oldExtension = Main.extensionManager.lookup(uuid);
let extensionDir = oldExtension.dir;
@ -145,8 +145,8 @@ function updateExtension(uuid) {
}
FileUtils.recursivelyDeleteDir(oldExtensionTmpDir, true);
}, (code, message) => {
log('Error while updating extension %s: %s (%s)'.format(uuid, code, message ? message : ''));
}, (code, msg) => {
log(`Error while updating extension ${uuid}: ${code} (${msg})`);
});
});
}
@ -162,7 +162,7 @@ function checkForUpdates() {
let url = REPOSITORY_URL_UPDATE;
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message, (session, message) => {
_httpSession.queue_message(message, () => {
if (message.status_code != Soup.KnownStatusCode.OK)
return;
@ -189,7 +189,7 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog {
this.setButtons([{
label: _("Cancel"),
action: this._onCancelButtonPressed.bind(this),
key: Clutter.Escape,
key: Clutter.KEY_Escape,
}, {
label: _("Install"),
action: this._onInstallButtonPressed.bind(this),
@ -199,8 +199,8 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog {
let content = new Dialog.MessageDialogContent({
title: _("Download and install “%s” from extensions.gnome.org?").format(info.name),
icon: new Gio.FileIcon({
file: Gio.File.new_for_uri(`${REPOSITORY_URL_BASE}${info.icon}`)
})
file: Gio.File.new_for_uri(`${REPOSITORY_URL_BASE}${info.icon}`),
}),
});
this.contentLayout.add(content);
@ -220,10 +220,9 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog {
let uuid = this._uuid;
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
let invocation = this._invocation;
function errback(code, message) {
let msg = message ? message.toString() : '';
log('Error while installing %s: %s (%s)'.format(uuid, code, msg));
invocation.return_dbus_error(`org.gnome.Shell.${code}`, msg);
function errback(code, msg) {
log(`Error while installing ${uuid}: ${code} (${msg})`);
invocation.return_dbus_error(`org.gnome.Shell.${code}`, msg || '');
}
function callback() {
@ -241,7 +240,7 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog {
invocation.return_value(GLib.Variant.new('(s)', ['successful']));
}
_httpSession.queue_message(message, (session, message) => {
_httpSession.queue_message(message, session => {
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
});

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported init connect disconnect */
const { Gio, St } = imports.gi;
const { GLib, Gio, St } = imports.gi;
const Signals = imports.signals;
const ExtensionUtils = imports.misc.extensionUtils;
@ -28,6 +28,23 @@ var ExtensionManager = class {
}
init() {
// The following file should exist for a period of time when extensions
// are enabled after start. If it exists, then the systemd unit will
// disable extensions should gnome-shell crash.
// Should the file already exist from a previous login, then this is OK.
let disableFilename = GLib.build_filenamev([GLib.get_user_runtime_dir(), 'gnome-shell-disable-extensions']);
let disableFile = Gio.File.new_for_path(disableFilename);
try {
disableFile.create(Gio.FileCreateFlags.REPLACE_DESTINATION, null);
} catch (e) {
log(`Failed to create file ${disableFilename}: ${e.message}`);
}
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 60, () => {
disableFile.delete(null);
return GLib.SOURCE_REMOVE;
});
this._sessionUpdated();
}
@ -60,11 +77,11 @@ var ExtensionManager = class {
let orderReversed = order.slice().reverse();
for (let i = 0; i < orderReversed.length; i++) {
let uuid = orderReversed[i];
let otherUuid = orderReversed[i];
try {
this.lookup(uuid).stateObj.disable();
this.lookup(otherUuid).stateObj.disable();
} catch (e) {
this.logExtensionError(uuid, e);
this.logExtensionError(otherUuid, e);
}
}
@ -81,11 +98,11 @@ var ExtensionManager = class {
}
for (let i = 0; i < order.length; i++) {
let uuid = order[i];
let otherUuid = order[i];
try {
this.lookup(uuid).stateObj.enable();
this.lookup(otherUuid).stateObj.enable();
} catch (e) {
this.logExtensionError(uuid, e);
this.logExtensionError(otherUuid, e);
}
}
@ -200,9 +217,8 @@ var ExtensionManager = class {
createExtensionObject(uuid, dir, type) {
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
if (!metadataFile.query_exists(null))
throw new Error('Missing metadata.json');
}
let metadataContents, success_;
try {
@ -222,14 +238,12 @@ var ExtensionManager = class {
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
if (!meta[prop])
throw new Error(`missing "${prop}" property in metadata.json`);
}
}
if (uuid != meta.uuid) {
if (uuid != meta.uuid)
throw new Error(`uuid "${meta.uuid}" from metadata.json does not match directory name "${uuid}"`);
}
let extension = {
metadata: meta,
@ -239,7 +253,7 @@ var ExtensionManager = class {
path: dir.get_path(),
error: '',
hasPrefs: dir.get_child('prefs.js').query_exists(null),
canChange: false
canChange: false,
};
this._extensions.set(uuid, extension);

View File

@ -195,7 +195,7 @@ var GrabHelper = class GrabHelper {
}
_takeModalGrab() {
let firstGrab = (this._modalCount == 0);
let firstGrab = this._modalCount == 0;
if (firstGrab) {
if (!Main.pushModal(this._owner, this._modalParams))
return false;
@ -292,7 +292,7 @@ var GrabHelper = class GrabHelper {
let touchEnd = type == Clutter.EventType.TOUCH_END;
let touch = touchUpdate || touchBegin || touchEnd;
if (touch && !global.display.is_pointer_emulating_sequence (event.get_event_sequence()))
if (touch && !global.display.is_pointer_emulating_sequence(event.get_event_sequence()))
return Clutter.EVENT_PROPAGATE;
if (this._ignoreUntilRelease && (motion || release || touch)) {

View File

@ -1,8 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported CandidatePopup */
const { Clutter, IBus, St } = imports.gi;
const Signals = imports.signals;
const { Clutter, GObject, IBus, St } = imports.gi;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
@ -12,11 +11,23 @@ var MAX_CANDIDATES_PER_PAGE = 16;
var DEFAULT_INDEX_LABELS = ['1', '2', '3', '4', '5', '6', '7', '8',
'9', '0', 'a', 'b', 'c', 'd', 'e', 'f'];
var CandidateArea = class CandidateArea {
constructor() {
this.actor = new St.BoxLayout({ vertical: true,
reactive: true,
visible: false });
var CandidateArea = GObject.registerClass({
Signals: {
'candidate-clicked': { param_types: [GObject.TYPE_UINT,
GObject.TYPE_UINT,
Clutter.ModifierType.$gtype] },
'cursor-down': {},
'cursor-up': {},
'next-page': {},
'previous-page': {},
},
}, class CandidateArea extends St.BoxLayout {
_init() {
super._init({
vertical: true,
reactive: true,
visible: false,
});
this._candidateBoxes = [];
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
let box = new St.BoxLayout({ style_class: 'candidate-box',
@ -24,10 +35,10 @@ var CandidateArea = class CandidateArea {
track_hover: true });
box._indexLabel = new St.Label({ style_class: 'candidate-index' });
box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
box.add(box._indexLabel, { y_fill: false });
box.add(box._candidateLabel, { y_fill: false });
box.add_child(box._indexLabel);
box.add_child(box._candidateLabel);
this._candidateBoxes.push(box);
this.actor.add(box);
this.add(box);
let j = i;
box.connect('button-release-event', (actor, event) => {
@ -36,30 +47,23 @@ var CandidateArea = class CandidateArea {
});
}
this.actor.connect('scroll-event', (actor, event) => {
let direction = event.get_scroll_direction();
switch (direction) {
case Clutter.ScrollDirection.UP:
this.emit('cursor-up');
break;
case Clutter.ScrollDirection.DOWN:
this.emit('cursor-down');
break;
}
return Clutter.EVENT_PROPAGATE;
});
this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous button' });
this._previousButton = new St.Button({
style_class: 'candidate-page-button candidate-page-button-previous button',
x_expand: true,
});
this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
this._buttonBox.add(this._previousButton, { expand: true });
this._buttonBox.add_child(this._previousButton);
this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next button' });
this._nextButton = new St.Button({
style_class: 'candidate-page-button candidate-page-button-next button',
x_expand: true,
});
this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
this._buttonBox.add(this._nextButton, { expand: true });
this._buttonBox.add_child(this._nextButton);
this.actor.add(this._buttonBox);
this.add(this._buttonBox);
this._previousButton.connect('clicked', () => {
this.emit('previous-page');
@ -72,6 +76,18 @@ var CandidateArea = class CandidateArea {
this._cursorPosition = 0;
}
vfunc_scroll_event(scrollEvent) {
switch (scrollEvent.direction) {
case Clutter.ScrollDirection.UP:
this.emit('cursor-up');
break;
case Clutter.ScrollDirection.DOWN:
this.emit('cursor-down');
break;
}
return Clutter.EVENT_PROPAGATE;
}
setOrientation(orientation) {
if (this._orientation == orientation)
return;
@ -79,15 +95,15 @@ var CandidateArea = class CandidateArea {
this._orientation = orientation;
if (this._orientation == IBus.Orientation.HORIZONTAL) {
this.actor.vertical = false;
this.actor.remove_style_class_name('vertical');
this.actor.add_style_class_name('horizontal');
this.vertical = false;
this.remove_style_class_name('vertical');
this.add_style_class_name('horizontal');
this._previousButton.child.icon_name = 'go-previous-symbolic';
this._nextButton.child.icon_name = 'go-next-symbolic';
} else { // VERTICAL || SYSTEM
this.actor.vertical = true;
this.actor.add_style_class_name('vertical');
this.actor.remove_style_class_name('horizontal');
this.vertical = true;
this.add_style_class_name('vertical');
this.remove_style_class_name('horizontal');
this._previousButton.child.icon_name = 'go-up-symbolic';
this._nextButton.child.icon_name = 'go-down-symbolic';
}
@ -102,7 +118,7 @@ var CandidateArea = class CandidateArea {
if (!visible)
continue;
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : DEFAULT_INDEX_LABELS[i]);
box._indexLabel.text = indexes && indexes[i] ? indexes[i] : DEFAULT_INDEX_LABELS[i];
box._candidateLabel.text = candidates[i];
}
@ -121,22 +137,23 @@ var CandidateArea = class CandidateArea {
this._previousButton.reactive = wrapsAround || page > 0;
this._nextButton.reactive = wrapsAround || page < nPages - 1;
}
};
Signals.addSignalMethods(CandidateArea.prototype);
});
var CandidatePopup = GObject.registerClass(
class IbusCandidatePopup extends BoxPointer.BoxPointer {
_init() {
super._init(St.Side.TOP);
this.visible = false;
this.style_class = 'candidate-popup-boxpointer';
var CandidatePopup = class CandidatePopup {
constructor() {
this._dummyCursor = new St.Widget({ opacity: 0 });
Main.layoutManager.uiGroup.add_actor(this._dummyCursor);
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
this._boxPointer.visible = false;
this._boxPointer.style_class = 'candidate-popup-boxpointer';
Main.layoutManager.addChrome(this._boxPointer);
Main.layoutManager.addChrome(this);
let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
vertical: true });
this._boxPointer.bin.set_child(box);
this.bin.set_child(box);
this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
visible: false });
@ -147,7 +164,7 @@ var CandidatePopup = class CandidatePopup {
box.add(this._auxText);
this._candidateArea = new CandidateArea();
box.add(this._candidateArea.actor);
box.add(this._candidateArea);
this._candidateArea.connect('previous-page', () => {
this._panelService.page_up();
@ -198,9 +215,10 @@ var CandidatePopup = class CandidatePopup {
this._preeditText.text = text.get_text();
let attrs = text.get_attributes();
if (attrs)
if (attrs) {
this._setTextAttributes(this._preeditText.clutter_text,
attrs);
}
});
panelService.connect('show-preedit-text', () => {
this._preeditText.show();
@ -225,14 +243,14 @@ var CandidatePopup = class CandidatePopup {
this._updateVisibility();
});
panelService.connect('update-lookup-table', (_ps, lookupTable, visible) => {
this._candidateArea.actor.visible = visible;
this._candidateArea.visible = visible;
this._updateVisibility();
let nCandidates = lookupTable.get_number_of_candidates();
let cursorPos = lookupTable.get_cursor_pos();
let pageSize = lookupTable.get_page_size();
let nPages = Math.ceil(nCandidates / pageSize);
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
let page = cursorPos == 0 ? 0 : Math.floor(cursorPos / pageSize);
let startIndex = page * pageSize;
let endIndex = Math.min((page + 1) * pageSize, nCandidates);
@ -261,15 +279,15 @@ var CandidatePopup = class CandidatePopup {
this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
});
panelService.connect('show-lookup-table', () => {
this._candidateArea.actor.show();
this._candidateArea.show();
this._updateVisibility();
});
panelService.connect('hide-lookup-table', () => {
this._candidateArea.actor.hide();
this._candidateArea.hide();
this._updateVisibility();
});
panelService.connect('focus-out', () => {
this._boxPointer.close(BoxPointer.PopupAnimation.NONE);
this.close(BoxPointer.PopupAnimation.NONE);
Main.keyboard.resetSuggestions();
});
}
@ -278,29 +296,30 @@ var CandidatePopup = class CandidatePopup {
this._dummyCursor.set_position(Math.round(x), Math.round(y));
this._dummyCursor.set_size(Math.round(w), Math.round(h));
if (this._boxPointer.visible)
this._boxPointer.setPosition(this._dummyCursor, 0);
if (this.visible)
this.setPosition(this._dummyCursor, 0);
}
_updateVisibility() {
let isVisible = (!Main.keyboard.visible &&
let isVisible = !Main.keyboard.visible &&
(this._preeditText.visible ||
this._auxText.visible ||
this._candidateArea.actor.visible));
this._candidateArea.visible);
if (isVisible) {
this._boxPointer.setPosition(this._dummyCursor, 0);
this._boxPointer.open(BoxPointer.PopupAnimation.NONE);
this._boxPointer.raise_top();
this.setPosition(this._dummyCursor, 0);
this.open(BoxPointer.PopupAnimation.NONE);
this.get_parent().set_child_above_sibling(this, null);
} else {
this._boxPointer.close(BoxPointer.PopupAnimation.NONE);
this.close(BoxPointer.PopupAnimation.NONE);
}
}
_setTextAttributes(clutterText, ibusAttrList) {
let attr;
for (let i = 0; (attr = ibusAttrList.get(i)); ++i)
for (let i = 0; (attr = ibusAttrList.get(i)); ++i) {
if (attr.get_attr_type() == IBus.AttrType.BACKGROUND)
clutterText.set_selection(attr.get_start_index(), attr.get_end_index());
}
}
};
});

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported BaseIcon, IconGrid, PaginatedIconGrid */
const { Clutter, GLib, GObject, Meta, St } = imports.gi;
const { Clutter, GLib, GObject, Graphene, Meta, St } = imports.gi;
const Params = imports.misc.params;
const Main = imports.ui.main;
@ -22,7 +22,7 @@ var ANIMATION_BOUNCE_ICON_SCALE = 1.1;
var AnimationDirection = {
IN: 0,
OUT: 1
OUT: 1,
};
var APPICON_ANIMATION_OUT_SCALE = 3;
@ -39,18 +39,19 @@ class BaseIcon extends St.Bin {
if (params.showLabel)
styleClass += ' overview-icon-with-label';
super._init({ style_class: styleClass,
x_fill: true,
y_fill: true });
super._init({ style_class: styleClass });
this.connect('destroy', this._onDestroy.bind(this));
this._box = new St.BoxLayout({ vertical: true });
this._box = new St.BoxLayout({
vertical: true,
x_expand: true,
y_expand: true,
});
this.set_child(this._box);
this.iconSize = ICON_SIZE;
this._iconBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._iconBin = new St.Bin();
this._box.add_actor(this._iconBin);
@ -58,7 +59,7 @@ class BaseIcon extends St.Bin {
this.label = new St.Label({ text: label });
this.label.clutter_text.set({
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER
y_align: Clutter.ActorAlign.CENTER,
});
this._box.add_actor(this.label);
} else {
@ -189,7 +190,7 @@ function zoomOutActorAtPos(actor, x, y) {
opacity: 0,
duration: APPICON_ANIMATION_OUT_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => actorClone.destroy()
onComplete: () => actorClone.destroy(),
});
}
@ -231,21 +232,21 @@ var IconGrid = GObject.registerClass({
this._fixedHItemSize = this._fixedVItemSize = undefined;
this.connect('style-changed', this._onStyleChanged.bind(this));
// Cancel animations when hiding the overview, to avoid icons
// swarming into the void ...
this.connect('notify::mapped', () => {
if (!this.mapped)
this._resetAnimationActors();
});
this.connect('actor-added', this._childAdded.bind(this));
this.connect('actor-removed', this._childRemoved.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
}
vfunc_unmap() {
// Cancel animations when hiding the overview, to avoid icons
// swarming into the void ...
this._resetAnimationActors();
super.vfunc_unmap();
}
_onDestroy() {
if (this._updateIconSizesLaterId) {
Meta.later_remove (this._updateIconSizesLaterId);
Meta.later_remove(this._updateIconSizesLaterId);
this._updateIconSizesLaterId = 0;
}
}
@ -402,7 +403,7 @@ var IconGrid = GObject.registerClass({
let allocationBox = this.get_allocation_box();
let paintBox = themeNode.get_paint_box(allocationBox);
let origin = new Clutter.Vertex();
let origin = new Graphene.Point3D();
origin.x = paintBox.x1 - allocationBox.x1;
origin.y = paintBox.y1 - allocationBox.y1;
origin.z = 0.0;
@ -431,7 +432,7 @@ var IconGrid = GObject.registerClass({
return true;
}
/**
/*
* Intended to be override by subclasses if they need a different
* set of items to be animated.
*/
@ -454,9 +455,10 @@ var IconGrid = GObject.registerClass({
}
animatePulse(animationDirection) {
if (animationDirection != AnimationDirection.IN)
if (animationDirection != AnimationDirection.IN) {
throw new GObject.NotImplementedError("Pulse animation only implements " +
"'in' animation direction");
}
this._resetAnimationActors();
@ -486,7 +488,7 @@ var IconGrid = GObject.registerClass({
scale_y: ANIMATION_BOUNCE_ICON_SCALE,
duration: bounceUpTime,
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
delay: delay,
delay,
onComplete: () => {
let duration = ANIMATION_TIME_IN - bounceUpTime;
actor.ease({
@ -498,9 +500,9 @@ var IconGrid = GObject.registerClass({
if (isLastItem)
this._animationDone();
actor.reactive = true;
}
},
});
}
},
});
}
}
@ -567,7 +569,7 @@ var IconGrid = GObject.registerClass({
scale_y: 1,
duration: ANIMATION_TIME_IN,
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
delay
delay,
};
if (isLastItem)
@ -577,7 +579,7 @@ var IconGrid = GObject.registerClass({
opacity: 255,
duration: ANIMATION_FADE_IN_TIME_FOR_ITEM,
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
delay
delay,
};
} else {
let isLastItem = actor._distance == maxDist;
@ -593,7 +595,7 @@ var IconGrid = GObject.registerClass({
scale_y: scaleY,
duration: ANIMATION_TIME_OUT,
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
delay
delay,
};
if (isLastItem)
@ -603,7 +605,7 @@ var IconGrid = GObject.registerClass({
opacity: 0,
duration: ANIMATION_FADE_IN_TIME_FOR_ITEM,
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
delay: ANIMATION_TIME_OUT + delay - ANIMATION_FADE_IN_TIME_FOR_ITEM
delay: ANIMATION_TIME_OUT + delay - ANIMATION_FADE_IN_TIME_FOR_ITEM,
};
}
@ -676,8 +678,8 @@ var IconGrid = GObject.registerClass({
nRows(forWidth) {
let children = this._getVisibleChildren();
let nColumns = (forWidth < 0) ? children.length : this._computeLayout(forWidth)[0];
let nRows = (nColumns > 0) ? Math.ceil(children.length / nColumns) : 0;
let nColumns = forWidth < 0 ? children.length : this._computeLayout(forWidth)[0];
let nRows = nColumns > 0 ? Math.ceil(children.length / nColumns) : 0;
if (this._rowLimit)
nRows = Math.min(nRows, this._rowLimit);
return nRows;
@ -717,13 +719,13 @@ var IconGrid = GObject.registerClass({
this._items.push(item);
if (index !== undefined)
this.insert_child_at_index(item.actor, index);
this.insert_child_at_index(item, index);
else
this.add_actor(item.actor);
this.add_actor(item);
}
removeItem(item) {
this.remove_child(item.actor);
this.remove_child(item);
}
getItemAtIndex(index) {
@ -783,7 +785,7 @@ var IconGrid = GObject.registerClass({
this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing;
}
/**
/*
* This function must to be called before iconGrid allocation,
* to know how much spacing can the grid has
*/
@ -796,7 +798,7 @@ var IconGrid = GObject.registerClass({
let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth;
let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight;
let neededSpacePerItem = (neededWidth > neededHeight)
let neededSpacePerItem = neededWidth > neededHeight
? Math.ceil(neededWidth / this._minColumns)
: Math.ceil(neededHeight / this._minRows);
this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE);
@ -804,9 +806,10 @@ var IconGrid = GObject.registerClass({
this._updateSpacingForSize(availWidth, availHeight);
}
if (!this._updateIconSizesLaterId)
if (!this._updateIconSizesLaterId) {
this._updateIconSizesLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
this._updateIconSizes.bind(this));
}
}
// Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
@ -814,9 +817,9 @@ var IconGrid = GObject.registerClass({
this._updateIconSizesLaterId = 0;
let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
let newIconSize = Math.floor(ICON_SIZE * scale);
for (let i in this._items) {
for (let i in this._items)
this._items[i].icon.setIconSize(newIconSize);
}
return GLib.SOURCE_REMOVE;
}
});
@ -878,9 +881,9 @@ var PaginatedIconGrid = GObject.registerClass({
children[i].show();
columnIndex++;
if (columnIndex == nColumns) {
if (columnIndex == nColumns)
columnIndex = 0;
}
if (columnIndex == 0) {
y += this._getVItemSize() + spacing;
if ((i + 1) % this._childrenPerPage == 0)
@ -955,15 +958,16 @@ var PaginatedIconGrid = GObject.registerClass({
/**
* openExtraSpace:
* @sourceItem: the item for which to create extra space
* @side: where @sourceItem should be located relative to the created space
* @nRows: the amount of space to create
* @param {Clutter.Actor} sourceItem: item for which to create extra space
* @param {St.Side} side: where @sourceItem should be located relative to
* the created space
* @param {number} nRows: the amount of space to create
*
* Pan view to create extra space for @nRows above or below @sourceItem.
*/
openExtraSpace(sourceItem, side, nRows) {
let children = this._getVisibleChildren();
let index = children.indexOf(sourceItem.actor);
let index = children.indexOf(sourceItem);
if (index == -1)
throw new Error('Item not found.');
@ -973,7 +977,7 @@ var PaginatedIconGrid = GObject.registerClass({
let childrenPerRow = this._childrenPerPage / this._rowsPerPage;
let sourceRow = Math.floor((index - pageOffset) / childrenPerRow);
let nRowsAbove = (side == St.Side.TOP) ? sourceRow + 1 : sourceRow;
let nRowsAbove = side == St.Side.TOP ? sourceRow + 1 : sourceRow;
let nRowsBelow = this._rowsPerPage - nRowsAbove;
let nRowsUp, nRowsDown;
@ -1016,7 +1020,7 @@ var PaginatedIconGrid = GObject.registerClass({
let params = {
translation_y: translationY,
duration: EXTRA_SPACE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
};
if (i == (children.length - 1))
params.onComplete = () => this.emit('space-opened');
@ -1037,7 +1041,7 @@ var PaginatedIconGrid = GObject.registerClass({
translation_y: 0,
duration: EXTRA_SPACE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
onComplete: () => this.emit('space-closed')
onComplete: () => this.emit('space-closed'),
});
}
}

View File

@ -18,8 +18,8 @@ var DialogResponse = Meta.InhibitShortcutsDialogResponse;
var InhibitShortcutsDialog = GObject.registerClass({
Implements: [Meta.InhibitShortcutsDialog],
Properties: {
'window': GObject.ParamSpec.override('window', Meta.InhibitShortcutsDialog)
}
'window': GObject.ParamSpec.override('window', Meta.InhibitShortcutsDialog),
},
}, class InhibitShortcutsDialog extends GObject.Object {
_init(window) {
super._init();
@ -84,10 +84,11 @@ var InhibitShortcutsDialog = GObject.registerClass({
let contentParams = { icon, title };
let restoreAccel = this._getRestoreAccel();
if (restoreAccel)
if (restoreAccel) {
contentParams.subtitle =
/* Translators: %s is a keyboard shortcut like "Super+x" */
_("You can restore shortcuts by pressing %s.").format(restoreAccel);
}
let content = new Dialog.MessageDialogContent(contentParams);
this._dialog.contentLayout.add_actor(content);
@ -134,10 +135,10 @@ var InhibitShortcutsDialog = GObject.registerClass({
this._permStore.LookupRemote(APP_PERMISSIONS_TABLE,
APP_PERMISSIONS_ID,
(res, error) => {
if (error) {
(res, err) => {
if (err) {
this._dialog.open();
log(error.message);
log(err.message);
return;
}

View File

@ -61,7 +61,7 @@ class KbdA11yDialog extends GObject.Object {
dialog.close();
},
default: enabled,
key: !enabled ? Clutter.Escape : null });
key: !enabled ? Clutter.KEY_Escape : null });
dialog.addButton({ label: enabled ? _("Turn Off") : _("Leave Off"),
action: () => {
@ -69,7 +69,7 @@ class KbdA11yDialog extends GObject.Object {
dialog.close();
},
default: !enabled,
key: enabled ? Clutter.Escape : null });
key: enabled ? Clutter.KEY_Escape : null });
dialog.open();
}

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@ var MonitorConstraint = GObject.registerClass({
'work-area': GObject.ParamSpec.boolean('work-area',
'Work-area', 'Track monitor\'s work-area',
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
false)
false),
},
}, class MonitorConstraint extends Clutter.Constraint {
_init(props) {
@ -167,12 +167,12 @@ var Monitor = class Monitor {
const UiActor = GObject.registerClass(
class UiActor extends St.Widget {
vfunc_get_preferred_width (_forHeight) {
vfunc_get_preferred_width(_forHeight) {
let width = global.stage.width;
return [width, width];
}
vfunc_get_preferred_height (_forWidth) {
vfunc_get_preferred_height(_forWidth) {
let height = global.stage.height;
return [height, height];
}
@ -181,7 +181,7 @@ class UiActor extends St.Widget {
const defaultParams = {
trackFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
affectsInputRegion: true,
};
var LayoutManager = GObject.registerClass({
@ -189,12 +189,13 @@ var LayoutManager = GObject.registerClass({
'startup-complete': {},
'startup-prepared': {},
'monitors-changed': {},
'system-modal-opened': {},
'keyboard-visible-changed': { param_types: [GObject.TYPE_BOOLEAN] } },
}, class LayoutManager extends GObject.Object {
_init() {
super._init();
this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
this.monitors = [];
this.primaryMonitor = null;
this.primaryIndex = -1;
@ -212,11 +213,6 @@ var LayoutManager = GObject.registerClass({
this._startingUp = true;
this._pendingLoadBackground = false;
// We don't want to paint the stage background color because either
// the SystemBackground we create or the MetaBackgroundActor inside
// global.window_group covers the entirety of the screen.
global.stage.no_clear_hint = true;
// Set up stage hierarchy to group all UI actors under one container.
this.uiGroup = new UiActor({ name: 'uiGroup' });
this.uiGroup.set_flags(Clutter.ActorFlags.NO_LAYOUT);
@ -274,11 +270,11 @@ var LayoutManager = GObject.registerClass({
this._backgroundGroup = new Meta.BackgroundGroup();
global.window_group.add_child(this._backgroundGroup);
this._backgroundGroup.lower_bottom();
global.window_group.set_child_below_sibling(this._backgroundGroup, null);
this._bgManagers = [];
this._interfaceSettings = new Gio.Settings({
schema_id: 'org.gnome.desktop.interface'
schema_id: 'org.gnome.desktop.interface',
});
this._interfaceSettings.connect('changed::enable-hot-corners',
@ -344,10 +340,11 @@ var LayoutManager = GObject.registerClass({
this.monitors = [];
let nMonitors = display.get_n_monitors();
for (let i = 0; i < nMonitors; i++)
for (let i = 0; i < nMonitors; i++) {
this.monitors.push(new Monitor(i,
display.get_monitor_geometry(i),
display.get_monitor_scale(i)));
}
if (nMonitors == 0) {
this.primaryIndex = this.bottomIndex = -1;
@ -450,7 +447,7 @@ var LayoutManager = GObject.registerClass({
_createBackgroundManager(monitorIndex) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
layoutManager: this,
monitorIndex: monitorIndex });
monitorIndex });
bgManager.connect('changed', this._addBackgroundMenu.bind(this));
this._addBackgroundMenu(bgManager);
@ -467,15 +464,14 @@ var LayoutManager = GObject.registerClass({
backgroundActor.ease({
opacity: 255,
duration: BACKGROUND_FADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
}
}
_updateBackgrounds() {
let i;
for (i = 0; i < this._bgManagers.length; i++)
for (let i = 0; i < this._bgManagers.length; i++)
this._bgManagers[i].destroy();
this._bgManagers = [];
@ -606,17 +602,17 @@ var LayoutManager = GObject.registerClass({
return;
}
this._systemBackground = new Background.SystemBackground();
this._systemBackground.actor.hide();
this._systemBackground.hide();
global.stage.insert_child_below(this._systemBackground.actor, null);
global.stage.insert_child_below(this._systemBackground, null);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._systemBackground.actor.add_constraint(constraint);
this._systemBackground.add_constraint(constraint);
let signalId = this._systemBackground.connect('loaded', () => {
this._systemBackground.disconnect(signalId);
this._systemBackground.actor.show();
this._systemBackground.show();
global.stage.show();
this._prepareStartupAnimation();
@ -703,7 +699,7 @@ var LayoutManager = GObject.registerClass({
translation_y: 0,
duration: STARTUP_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._startupAnimationComplete()
onComplete: () => this._startupAnimationComplete(),
});
}
@ -714,7 +710,7 @@ var LayoutManager = GObject.registerClass({
opacity: 255,
duration: STARTUP_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._startupAnimationComplete()
onComplete: () => this._startupAnimationComplete(),
});
}
@ -722,7 +718,7 @@ var LayoutManager = GObject.registerClass({
this._coverPane.destroy();
this._coverPane = null;
this._systemBackground.actor.destroy();
this._systemBackground.destroy();
this._systemBackground = null;
this._startingUp = false;
@ -748,7 +744,7 @@ var LayoutManager = GObject.registerClass({
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._showKeyboardComplete();
}
},
});
this.emit('keyboard-visible-changed', true);
}
@ -775,7 +771,7 @@ var LayoutManager = GObject.registerClass({
mode: Clutter.AnimationMode.EASE_IN_QUAD,
onComplete: () => {
this._hideKeyboardComplete();
}
},
});
this.emit('keyboard-visible-changed', false);
@ -961,7 +957,7 @@ var LayoutManager = GObject.registerClass({
findIndexForActor(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h });
let rect = new Meta.Rectangle({ x, y, width: w, height: h });
return global.display.get_monitor_index_for_rect(rect);
}
@ -976,9 +972,10 @@ var LayoutManager = GObject.registerClass({
if (this._startingUp)
return;
if (!this._updateRegionIdle)
if (!this._updateRegionIdle) {
this._updateRegionIdle = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
this._updateRegions.bind(this));
}
}
_getWindowActorsForWorkspace(workspace) {
@ -1032,7 +1029,7 @@ var LayoutManager = GObject.registerClass({
h = Math.round(h);
if (actorData.affectsInputRegion && wantsInputRegion && actorData.actor.get_paint_visibility())
rects.push(new Meta.Rectangle({ x: x, y: y, width: w, height: h }));
rects.push(new Meta.Rectangle({ x, y, width: w, height: h }));
let monitor = null;
if (actorData.affectsStruts)
@ -1083,7 +1080,7 @@ var LayoutManager = GObject.registerClass({
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1 });
let strut = new Meta.Strut({ rect: strutRect, side: side });
let strut = new Meta.Strut({ rect: strutRect, side });
struts.push(strut);
}
}
@ -1112,8 +1109,11 @@ var LayoutManager = GObject.registerClass({
//
// This class manages a "hot corner" that can toggle switching to
// overview.
var HotCorner = class HotCorner {
constructor(layoutManager, monitor, x, y) {
var HotCorner = GObject.registerClass(
class HotCorner extends Clutter.Actor {
_init(layoutManager, monitor, x, y) {
super._init();
// We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding
// guard area (the "environs"). This avoids triggering the hot corner
@ -1142,6 +1142,8 @@ var HotCorner = class HotCorner {
this._ripples = new Ripples.Ripples(px, py, 'ripple-box');
this._ripples.addTo(layoutManager.uiGroup);
this.connect('destroy', this._onDestroy.bind(this));
}
setBarrierSize(size) {
@ -1181,11 +1183,14 @@ var HotCorner = class HotCorner {
_setupFallbackCornerIfNeeded(layoutManager) {
if (!global.display.supports_extended_barriers()) {
this.actor = new Clutter.Actor({ name: 'hot-corner-environs',
x: this._x, y: this._y,
width: 3,
height: 3,
reactive: true });
this.set({
name: 'hot-corner-environs',
x: this._x,
y: this._y,
width: 3,
height: 3,
reactive: true,
});
this._corner = new Clutter.Actor({ name: 'hot-corner',
width: 1,
@ -1194,19 +1199,16 @@ var HotCorner = class HotCorner {
reactive: true });
this._corner._delegate = this;
this.actor.add_child(this._corner);
layoutManager.addChrome(this.actor);
this.add_child(this._corner);
layoutManager.addChrome(this);
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
this._corner.set_position(this.actor.width - this._corner.width, 0);
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
this._corner.set_position(this.width - this._corner.width, 0);
this.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
} else {
this._corner.set_position(0, 0);
}
this.actor.connect('leave-event',
this._onEnvironsLeft.bind(this));
this._corner.connect('enter-event',
this._onCornerEntered.bind(this));
this._corner.connect('leave-event',
@ -1214,14 +1216,11 @@ var HotCorner = class HotCorner {
}
}
destroy() {
_onDestroy() {
this.setBarrierSize(0);
this._pressureBarrier.destroy();
this._pressureBarrier = null;
if (this.actor)
this.actor.destroy();
this._ripples.destroy();
}
@ -1253,18 +1252,18 @@ var HotCorner = class HotCorner {
}
_onCornerLeft(actor, event) {
if (event.get_related() != this.actor)
if (event.get_related() != this)
this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft
return Clutter.EVENT_STOP;
}
_onEnvironsLeft(actor, event) {
if (event.get_related() != this._corner)
vfunc_leave_event(crossingEvent) {
if (crossingEvent.related != this._corner)
this._entered = false;
return Clutter.EVENT_PROPAGATE;
}
};
});
var PressureBarrier = class PressureBarrier {
constructor(threshold, timeout, actionMode) {

View File

@ -2,7 +2,6 @@
/* exported Lightbox */
const { Clutter, GObject, Shell, St } = imports.gi;
const Signals = imports.signals;
const Params = imports.misc.params;
@ -34,8 +33,8 @@ var RadialShaderEffect = GObject.registerClass({
'sharpness', 'sharpness', 'sharpness',
GObject.ParamFlags.READWRITE,
0, 1, 0
)
}
),
},
}, class RadialShaderEffect extends Shell.GLSLEffect {
_init(params) {
this._brightness = undefined;
@ -89,8 +88,8 @@ var RadialShaderEffect = GObject.registerClass({
* - inhibitEvents: whether to inhibit events for @container
* - width: shade actor width
* - height: shade actor height
* - fadeInTime: milliseconds used to fade in
* - fadeOutTime: milliseconds used to fade out
* - fadeFactor: fading opacity factor
* - radialEffect: whether to enable the GLSL radial effect
*
* Lightbox creates a dark translucent "shade" actor to hide the
* contents of @container, and allows you to specify particular actors
@ -106,8 +105,13 @@ var RadialShaderEffect = GObject.registerClass({
* @container and will track any changes in its size. You can override
* this by passing an explicit width and height in @params.
*/
var Lightbox = class Lightbox {
constructor(container, params) {
var Lightbox = GObject.registerClass({
Properties: {
'active': GObject.ParamSpec.boolean(
'active', 'active', 'active', GObject.ParamFlags.READABLE, false),
},
}, class Lightbox extends St.Bin {
_init(container, params) {
params = Params.parse(params, {
inhibitEvents: false,
width: null,
@ -116,32 +120,34 @@ var Lightbox = class Lightbox {
radialEffect: false,
});
super._init({
reactive: params.inhibitEvents,
width: params.width,
height: params.height,
visible: false,
});
this._active = false;
this._container = container;
this._children = container.get_children();
this._fadeFactor = params.fadeFactor;
this._radialEffect = Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL) && params.radialEffect;
this.actor = new St.Bin({ reactive: params.inhibitEvents });
if (this._radialEffect)
this.actor.add_effect(new RadialShaderEffect({ name: 'radial' }));
this.add_effect(new RadialShaderEffect({ name: 'radial' }));
else
this.actor.set({ opacity: 0, style_class: 'lightbox' });
this.set({ opacity: 0, style_class: 'lightbox' });
container.add_actor(this.actor);
this.actor.raise_top();
this.actor.hide();
this.shown = false;
container.add_actor(this);
container.set_child_above_sibling(this, null);
this.actor.connect('destroy', this._onDestroy.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
if (params.width && params.height) {
this.actor.width = params.width;
this.actor.height = params.height;
} else {
let constraint = new Clutter.BindConstraint({ source: container,
coordinate: Clutter.BindCoordinate.ALL });
this.actor.add_constraint(constraint);
if (!params.width || !params.height) {
this.add_constraint(new Clutter.BindConstraint({
source: container,
coordinate: Clutter.BindCoordinate.ALL,
}));
}
this._actorAddedSignalId = container.connect('actor-added', this._actorAdded.bind(this));
@ -150,16 +156,20 @@ var Lightbox = class Lightbox {
this._highlighted = null;
}
get active() {
return this._active;
}
_actorAdded(container, newChild) {
let children = this._container.get_children();
let myIndex = children.indexOf(this.actor);
let myIndex = children.indexOf(this);
let newChildIndex = children.indexOf(newChild);
if (newChildIndex > myIndex) {
// The child was added above the shade (presumably it was
// made the new top-most child). Move it below the shade,
// and add it to this._children as the new topmost actor.
newChild.lower(this.actor);
this._container.set_child_above_sibling(this, newChild);
this._children.push(newChild);
} else if (newChildIndex == 0) {
// Bottom of stack
@ -172,53 +182,55 @@ var Lightbox = class Lightbox {
}
}
show(fadeInTime) {
this.actor.remove_all_transitions();
lightOn(fadeInTime) {
this.remove_all_transitions();
let easeProps = {
duration: fadeInTime || 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
};
let onComplete = () => {
this.shown = true;
this.emit('shown');
this._active = true;
this.notify('active');
};
this.actor.show();
this.show();
if (this._radialEffect) {
this.actor.ease_property(
this.ease_property(
'@effects.radial.brightness', VIGNETTE_BRIGHTNESS, easeProps);
this.actor.ease_property(
this.ease_property(
'@effects.radial.sharpness', VIGNETTE_SHARPNESS,
Object.assign({ onComplete }, easeProps));
} else {
this.actor.ease(Object.assign(easeProps, {
this.ease(Object.assign(easeProps, {
opacity: 255 * this._fadeFactor,
onComplete
onComplete,
}));
}
}
hide(fadeOutTime) {
this.shown = false;
this.actor.remove_all_transitions();
lightOff(fadeOutTime) {
this.remove_all_transitions();
this._active = false;
this.notify('active');
let easeProps = {
duration: fadeOutTime || 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
};
let onComplete = () => this.actor.hide();
let onComplete = () => this.hide();
if (this._radialEffect) {
this.actor.ease_property(
this.ease_property(
'@effects.radial.brightness', 1.0, easeProps);
this.actor.ease_property(
this.ease_property(
'@effects.radial.sharpness', 0.0, Object.assign({ onComplete }, easeProps));
} else {
this.actor.ease(Object.assign(easeProps, { opacity: 0, onComplete }));
this.ease(Object.assign(easeProps, { opacity: 0, onComplete }));
}
}
@ -233,7 +245,7 @@ var Lightbox = class Lightbox {
/**
* highlight:
* @window: actor to highlight
* @param {Clutter.Actor=} window: actor to highlight
*
* Highlights the indicated actor and unhighlights any other
* currently-highlighted actor. With no arguments or a false/null
@ -249,12 +261,12 @@ var Lightbox = class Lightbox {
// case we may need to indicate some *other* actor as the new
// sibling of the to-be-lowered one.
let below = this.actor;
let below = this;
for (let i = this._children.length - 1; i >= 0; i--) {
if (this._children[i] == window)
this._children[i].raise_top();
this._container.set_child_above_sibling(this._children[i], null);
else if (this._children[i] == this._highlighted)
this._children[i].lower(below);
this._container.set_child_below_sibling(this._children[i], below);
else
below = this._children[i];
}
@ -262,15 +274,6 @@ var Lightbox = class Lightbox {
this._highlighted = window;
}
/**
* destroy:
*
* Destroys the lightbox.
*/
destroy() {
this.actor.destroy();
}
/**
* _onDestroy:
*
@ -278,10 +281,15 @@ var Lightbox = class Lightbox {
* by destroying its container or by explicitly calling this.destroy().
*/
_onDestroy() {
this._container.disconnect(this._actorAddedSignalId);
this._container.disconnect(this._actorRemovedSignalId);
if (this._actorAddedSignalId) {
this._container.disconnect(this._actorAddedSignalId);
this._actorAddedSignalId = 0;
}
if (this._actorRemovedSignalId) {
this._container.disconnect(this._actorRemovedSignalId);
this._actorRemovedSignalId = 0;
}
this.highlight(null);
}
};
Signals.addSignalMethods(Lightbox.prototype);
});

View File

@ -1,8 +1,8 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported LookingGlass */
const { Clutter, Cogl, Gio, GLib,
GObject, Meta, Pango, Shell, St } = imports.gi;
const { Clutter, Cogl, Gio, GLib, GObject,
Graphene, Meta, Pango, Shell, St } = imports.gi;
const Signals = imports.signals;
const System = imports.system;
@ -54,9 +54,9 @@ var AutoComplete = class AutoComplete {
}
_processCompletionRequest(event) {
if (event.completions.length == 0) {
if (event.completions.length == 0)
return;
}
// Unique match = go ahead and complete; multiple matches + single tab = complete the common starting string;
// multiple matches + double tab = emit a suggest event with all possible options
if (event.completions.length == 1) {
@ -78,20 +78,20 @@ var AutoComplete = class AutoComplete {
_entryKeyPressEvent(actor, event) {
let cursorPos = this._entry.clutter_text.get_cursor_position();
let text = this._entry.get_text();
if (cursorPos != -1) {
if (cursorPos != -1)
text = text.slice(0, cursorPos);
}
if (event.get_key_symbol() == Clutter.Tab) {
if (event.get_key_symbol() == Clutter.KEY_Tab) {
let [completions, attrHead] = JsParse.getCompletions(text, commandHeader, AUTO_COMPLETE_GLOBAL_KEYWORDS);
let currTime = global.get_current_time();
if ((currTime - this._lastTabTime) < AUTO_COMPLETE_DOUBLE_TAB_DELAY) {
this._processCompletionRequest({ tabType: 'double',
completions: completions,
attrHead: attrHead });
completions,
attrHead });
} else {
this._processCompletionRequest({ tabType: 'single',
completions: completions,
attrHead: attrHead });
completions,
attrHead });
}
this._lastTabTime = currTime;
}
@ -110,9 +110,14 @@ var AutoComplete = class AutoComplete {
Signals.addSignalMethods(AutoComplete.prototype);
var Notebook = class Notebook {
constructor() {
this.actor = new St.BoxLayout({ vertical: true });
var Notebook = GObject.registerClass({
Signals: { 'selection': { param_types: [Clutter.Actor.$gtype] } },
}, class Notebook extends St.BoxLayout {
_init() {
super._init({
vertical: true,
y_expand: true,
});
this.tabControls = new St.BoxLayout({ style_class: 'labels' });
@ -129,21 +134,21 @@ var Notebook = class Notebook {
this.selectChild(child);
return true;
});
labelBox.add(label, { expand: true });
labelBox.add_child(label);
this.tabControls.add(labelBox);
let scrollview = new St.ScrollView({ x_fill: true, y_fill: true });
let scrollview = new St.ScrollView({ y_expand: true });
scrollview.get_hscroll_bar().hide();
scrollview.add_actor(child);
let tabData = { child: child,
labelBox: labelBox,
label: label,
let tabData = { child,
labelBox,
label,
scrollView: scrollview,
_scrollToBottom: false };
this._tabs.push(tabData);
scrollview.hide();
this.actor.add(scrollview, { expand: true });
this.add_child(scrollview);
let vAdjust = scrollview.vscroll.adjustment;
vAdjust.connect('changed', () => this._onAdjustScopeChanged(tabData));
@ -174,7 +179,7 @@ var Notebook = class Notebook {
// Focus the new tab before unmapping the old one
let tabData = this._tabs[index];
if (!tabData.scrollView.navigate_focus(null, St.DirectionType.TAB_FORWARD, false))
this.actor.grab_key_focus();
this.grab_key_focus();
this._unselect();
@ -219,23 +224,20 @@ var Notebook = class Notebook {
nextTab() {
let nextIndex = this._selectedIndex;
if (nextIndex < this._tabs.length - 1) {
if (nextIndex < this._tabs.length - 1)
++nextIndex;
}
this.selectIndex(nextIndex);
}
prevTab() {
let prevIndex = this._selectedIndex;
if (prevIndex > 0) {
if (prevIndex > 0)
--prevIndex;
}
this.selectIndex(prevIndex);
}
};
Signals.addSignalMethods(Notebook.prototype);
});
function objectToString(o) {
if (typeof o == typeof objectToString) {
@ -246,57 +248,63 @@ function objectToString(o) {
}
}
var ObjLink = class ObjLink {
constructor(lookingGlass, o, title) {
var ObjLink = GObject.registerClass(
class ObjLink extends St.Button {
_init(lookingGlass, o, title) {
let text;
if (title)
text = title;
else
text = objectToString(o);
text = GLib.markup_escape_text(text, -1);
super._init({
reactive: true,
track_hover: true,
style_class: 'shell-link',
label: text,
x_align: Clutter.ActorAlign.START,
});
this.get_child().single_line_mode = true;
this._obj = o;
this.actor = new St.Button({ reactive: true,
track_hover: true,
style_class: 'shell-link',
label: text });
this.actor.get_child().single_line_mode = true;
this.actor.connect('clicked', this._onClicked.bind(this));
this._lookingGlass = lookingGlass;
}
_onClicked() {
this._lookingGlass.inspectObject(this._obj, this.actor);
vfunc_clicked() {
this._lookingGlass.inspectObject(this._obj, this);
}
};
});
var Result = GObject.registerClass(
class Result extends St.BoxLayout {
_init(lookingGlass, command, o, index) {
super._init({ vertical: true });
var Result = class Result {
constructor(lookingGlass, command, o, index) {
this.index = index;
this.o = o;
this.actor = new St.BoxLayout({ vertical: true });
this._lookingGlass = lookingGlass;
let cmdTxt = new St.Label({ text: command });
cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
this.actor.add(cmdTxt);
this.add(cmdTxt);
let box = new St.BoxLayout({});
this.actor.add(box);
this.add(box);
let resultTxt = new St.Label({ text: `r(${index}) = ` });
resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
box.add(resultTxt);
let objLink = new ObjLink(this._lookingGlass, o);
box.add(objLink.actor);
box.add(objLink);
}
};
});
var WindowList = class WindowList {
constructor(lookingGlass) {
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
var WindowList = GObject.registerClass({
}, class WindowList extends St.BoxLayout {
_init(lookingGlass) {
super._init({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
let tracker = Shell.WindowTracker.get_default();
this._updateId = Main.initializeDeferredWork(this.actor, this._updateWindowList.bind(this));
this._updateId = Main.initializeDeferredWork(this, this._updateWindowList.bind(this));
global.display.connect('window-created', this._updateWindowList.bind(this));
tracker.connect('tracked-windows-changed', this._updateWindowList.bind(this));
@ -307,7 +315,7 @@ var WindowList = class WindowList {
if (!this._lookingGlass.isOpen)
return;
this.actor.destroy_all_children();
this.destroy_all_children();
let windows = global.get_window_actors();
let tracker = Shell.WindowTracker.get_default();
for (let i = 0; i < windows.length; i++) {
@ -318,9 +326,9 @@ var WindowList = class WindowList {
metaWindow._lookingGlassManaged = true;
}
let box = new St.BoxLayout({ vertical: true });
this.actor.add(box);
this.add(box);
let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title);
box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
box.add_child(windowLink);
let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
box.add(propsBox);
propsBox.add(new St.Label({ text: `wmclass: ${metaWindow.get_wm_class()}` }));
@ -329,10 +337,10 @@ var WindowList = class WindowList {
let icon = app.create_icon_texture(22);
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
propsBox.add(propBox);
propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false });
propBox.add_child(new St.Label({ text: 'app: ' }));
let appLink = new ObjLink(this._lookingGlass, app, app.get_id());
propBox.add(appLink.actor, { y_fill: false });
propBox.add(icon, { y_fill: false });
propBox.add_child(appLink);
propBox.add_child(icon);
} else {
propsBox.add(new St.Label({ text: '<untracked>' }));
}
@ -342,23 +350,29 @@ var WindowList = class WindowList {
update() {
this._updateWindowList();
}
};
Signals.addSignalMethods(WindowList.prototype);
});
var ObjInspector = GObject.registerClass(
class ObjInspector extends St.ScrollView {
_init(lookingGlass) {
super._init({
pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
});
var ObjInspector = class ObjInspector {
constructor(lookingGlass) {
this._obj = null;
this._previousObj = null;
this._parentList = [];
this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
x_fill: true, y_fill: true });
this.actor.get_hscroll_bar().hide();
this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
style_class: 'lg-dialog',
vertical: true });
this.actor.add_actor(this._container);
this.get_hscroll_bar().hide();
this._container = new St.BoxLayout({
name: 'LookingGlassPropertyInspector',
style_class: 'lg-dialog',
vertical: true,
x_expand: true,
y_expand: true,
});
this.add_actor(this._container);
this._lookingGlass = lookingGlass;
}
@ -374,10 +388,12 @@ var ObjInspector = class ObjInspector {
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
this._container.add_actor(hbox);
let label = new St.Label({ text: 'Inspecting: %s: %s'.format(typeof obj,
objectToString(obj)) });
let label = new St.Label({
text: 'Inspecting: %s: %s'.format(typeof obj, objectToString(obj)),
x_expand: true,
});
label.single_line_mode = true;
hbox.add(label, { expand: true, y_fill: false });
hbox.add_child(label);
let button = new St.Button({ label: 'Insert', style_class: 'lg-obj-inspector-button' });
button.connect('clicked', this._onInsert.bind(this));
hbox.add(button);
@ -394,9 +410,8 @@ var ObjInspector = class ObjInspector {
hbox.add(button);
if (typeof obj == typeof {}) {
let properties = [];
for (let propName in obj) {
for (let propName in obj)
properties.push(propName);
}
properties.sort();
for (let i = 0; i < properties.length; i++) {
@ -404,14 +419,14 @@ var ObjInspector = class ObjInspector {
let link;
try {
let prop = obj[propName];
link = new ObjLink(this._lookingGlass, prop).actor;
link = new ObjLink(this._lookingGlass, prop);
} catch (e) {
link = new St.Label({ text: '<error>' });
}
let hbox = new St.BoxLayout();
hbox.add(new St.Label({ text: `${propName}: ` }));
hbox.add(link);
this._container.add_actor(hbox);
let box = new St.BoxLayout();
box.add(new St.Label({ text: `${propName}: ` }));
box.add(link);
this._container.add_actor(box);
}
}
}
@ -421,17 +436,17 @@ var ObjInspector = class ObjInspector {
return;
this._previousObj = null;
this._open = true;
this.actor.show();
this.show();
if (sourceActor) {
this.actor.set_scale(0, 0);
this.actor.ease({
this.set_scale(0, 0);
this.ease({
scale_x: 1,
scale_y: 1,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: 200
duration: 200,
});
} else {
this.actor.set_scale(1, 1);
this.set_scale(1, 1);
}
}
@ -439,7 +454,7 @@ var ObjInspector = class ObjInspector {
if (!this._open)
return;
this._open = false;
this.actor.hide();
this.hide();
this._previousObj = null;
this._obj = null;
}
@ -453,29 +468,44 @@ var ObjInspector = class ObjInspector {
_onBack() {
this.selectObject(this._previousObj, true);
}
};
});
var RedBorderEffect = GObject.registerClass(
class RedBorderEffect extends Clutter.Effect {
vfunc_paint() {
_init() {
super._init();
this._pipeline = null;
}
vfunc_paint(paintContext) {
let framebuffer = paintContext.get_framebuffer();
let coglContext = framebuffer.get_context();
let actor = this.get_actor();
actor.continue_paint();
actor.continue_paint(paintContext);
let color = new Cogl.Color();
color.init_from_4ub(0xff, 0, 0, 0xc4);
Cogl.set_source_color(color);
if (!this._pipeline) {
let color = new Cogl.Color();
color.init_from_4ub(0xff, 0, 0, 0xc4);
let geom = actor.get_allocation_geometry();
this._pipeline = new Cogl.Pipeline(coglContext);
this._pipeline.set_color(color);
}
let alloc = actor.get_allocation_box();
let width = 2;
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
framebuffer.draw_rectangle(this._pipeline,
0, 0, alloc.get_width(), width);
framebuffer.draw_rectangle(this._pipeline,
alloc.get_width() - width, width,
alloc.get_width(), alloc.get_height());
framebuffer.draw_rectangle(this._pipeline,
0, alloc.get_height(),
alloc.get_width() - width, alloc.get_height() - width);
framebuffer.draw_rectangle(this._pipeline,
0, alloc.get_height() - width,
width, width);
}
});
@ -484,8 +514,7 @@ var Inspector = GObject.registerClass({
'target': { param_types: [Clutter.Actor.$gtype, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] } },
}, class Inspector extends Clutter.Actor {
_init(lookingGlass) {
super._init({ width: 0,
height: 0 });
super._init({ width: 0, height: 0 });
Main.uiGroup.add_actor(this);
@ -494,8 +523,8 @@ var Inspector = GObject.registerClass({
reactive: true });
this._eventHandler = eventHandler;
this.add_actor(eventHandler);
this._displayText = new St.Label();
eventHandler.add(this._displayText, { expand: true });
this._displayText = new St.Label({ x_expand: true });
eventHandler.add_child(this._displayText);
eventHandler.connect('key-press-event', this._onKeyPressEvent.bind(this));
eventHandler.connect('button-press-event', this._onButtonPressEvent.bind(this));
@ -548,7 +577,7 @@ var Inspector = GObject.registerClass({
}
_onKeyPressEvent(actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
if (event.get_key_symbol() === Clutter.KEY_Escape)
this._close();
return Clutter.EVENT_STOP;
}
@ -620,18 +649,19 @@ var Inspector = GObject.registerClass({
}
});
var Extensions = class Extensions {
constructor(lookingGlass) {
var Extensions = GObject.registerClass({
}, class Extensions extends St.BoxLayout {
_init(lookingGlass) {
super._init({ vertical: true, name: 'lookingGlassExtensions' });
this._lookingGlass = lookingGlass;
this.actor = new St.BoxLayout({ vertical: true,
name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
text: _("No extensions installed") });
this._numExtensions = 0;
this._extensionsList = new St.BoxLayout({ vertical: true,
style_class: 'lg-extensions-list' });
this._extensionsList.add(this._noExtensions);
this.actor.add(this._extensionsList);
this.add(this._extensionsList);
Main.extensionManager.getUuids().forEach(uuid => {
this._loadExtension(null, uuid);
@ -652,7 +682,7 @@ var Extensions = class Extensions {
if (this._numExtensions == 0)
this._extensionsList.remove_actor(this._noExtensions);
this._numExtensions ++;
this._numExtensions++;
this._extensionsList.add(extensionDisplay);
}
@ -677,7 +707,7 @@ var Extensions = class Extensions {
let errors = extension.errors;
let errorDisplay = new St.BoxLayout({ vertical: true });
if (errors && errors.length) {
for (let i = 0; i < errors.length; i ++)
for (let i = 0; i < errors.length; i++)
errorDisplay.add(new St.Label({ text: errors[i] }));
} else {
/* Translators: argument is an extension UUID. */
@ -716,12 +746,18 @@ var Extensions = class Extensions {
_createExtensionDisplay(extension) {
let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
let name = new St.Label({ style_class: 'lg-extension-name',
text: extension.metadata.name });
box.add(name, { expand: true });
let description = new St.Label({ style_class: 'lg-extension-description',
text: extension.metadata.description || 'No description' });
box.add(description, { expand: true });
let name = new St.Label({
style_class: 'lg-extension-name',
text: extension.metadata.name,
x_expand: true,
});
box.add_child(name);
let description = new St.Label({
style_class: 'lg-extension-description',
text: extension.metadata.description || 'No description',
x_expand: true,
});
box.add_child(description);
let metaBox = new St.BoxLayout({ style_class: 'lg-extension-meta' });
box.add(metaBox);
@ -759,10 +795,19 @@ var Extensions = class Extensions {
return box;
}
};
});
var LookingGlass = GObject.registerClass(
class LookingGlass extends St.BoxLayout {
_init() {
super._init({
name: 'LookingGlassDialog',
style_class: 'lg-dialog',
vertical: true,
visible: false,
reactive: true,
});
var LookingGlass = class LookingGlass {
constructor() {
this._borderPaintTarget = null;
this._redBorderEffect = new RedBorderEffect();
@ -770,26 +815,18 @@ var LookingGlass = class LookingGlass {
this._it = null;
this._offset = 0;
this._results = [];
// Sort of magic, but...eh.
this._maxItems = 150;
this.actor = new St.BoxLayout({ name: 'LookingGlassDialog',
style_class: 'lg-dialog',
vertical: true,
visible: false,
reactive: true });
this.actor.connect('key-press-event', this._globalKeyPressEvent.bind(this));
this._interfaceSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
this._interfaceSettings.connect('changed::monospace-font-name',
this._updateFont.bind(this));
this._updateFont();
// We want it to appear to slide out from underneath the panel
Main.uiGroup.add_actor(this.actor);
Main.uiGroup.set_child_below_sibling(this.actor,
Main.uiGroup.add_actor(this);
Main.uiGroup.set_child_below_sibling(this,
Main.layoutManager.panelBox);
Main.layoutManager.panelBox.connect('allocation-changed',
this._queueResize.bind(this));
@ -797,11 +834,11 @@ var LookingGlass = class LookingGlass {
this._queueResize.bind(this));
this._objInspector = new ObjInspector(this);
Main.uiGroup.add_actor(this._objInspector.actor);
this._objInspector.actor.hide();
Main.uiGroup.add_actor(this._objInspector);
this._objInspector.hide();
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
this.actor.add_actor(toolbar);
this.add_actor(toolbar);
let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
icon_size: 24 });
toolbar.add_actor(inspectIcon);
@ -812,10 +849,10 @@ var LookingGlass = class LookingGlass {
this._pushResult(`inspect(${Math.round(stageX)}, ${Math.round(stageY)})`, target);
});
inspector.connect('closed', () => {
this.actor.show();
this.show();
global.stage.set_key_focus(this._entry);
});
this.actor.hide();
this.hide();
return Clutter.EVENT_STOP;
});
@ -837,33 +874,43 @@ var LookingGlass = class LookingGlass {
let notebook = new Notebook();
this._notebook = notebook;
this.actor.add(notebook.actor, { expand: true });
this.add_child(notebook);
let emptyBox = new St.Bin();
toolbar.add(emptyBox, { expand: true });
let emptyBox = new St.Bin({ x_expand: true });
toolbar.add_child(emptyBox);
toolbar.add_actor(notebook.tabControls);
this._evalBox = new St.BoxLayout({ name: 'EvalBox', vertical: true });
notebook.appendPage('Evaluator', this._evalBox);
this._resultsArea = new St.BoxLayout({ name: 'ResultsArea', vertical: true });
this._evalBox.add(this._resultsArea, { expand: true });
this._resultsArea = new St.BoxLayout({
name: 'ResultsArea',
vertical: true,
y_expand: true,
});
this._evalBox.add_child(this._resultsArea);
this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
this._entryArea = new St.BoxLayout({
name: 'EntryArea',
y_align: Clutter.ActorAlign.END,
});
this._evalBox.add_actor(this._entryArea);
let label = new St.Label({ text: CHEVRON });
this._entryArea.add(label);
this._entry = new St.Entry({ can_focus: true });
this._entry = new St.Entry({
can_focus: true,
x_expand: true,
});
ShellEntry.addContextMenu(this._entry);
this._entryArea.add(this._entry, { expand: true });
this._entryArea.add_child(this._entry);
this._windowList = new WindowList(this);
notebook.appendPage('Windows', this._windowList.actor);
notebook.appendPage('Windows', this._windowList);
this._extensions = new Extensions(this);
notebook.appendPage('Extensions', this._extensions.actor);
notebook.appendPage('Extensions', this._extensions);
this._entry.clutter_text.connect('activate', (o, _e) => {
// Hide any completions we are currently showing
@ -905,7 +952,7 @@ var LookingGlass = class LookingGlass {
// monospace font to be bold/oblique/etc. Could easily be added here.
let size = fontDesc.get_size() / 1024.;
let unit = fontDesc.get_size_is_absolute() ? 'px' : 'pt';
this.actor.style = `
this.style = `
font-size: ${size}${unit};
font-family: "${fontDesc.get_family()}";`;
}
@ -919,17 +966,14 @@ var LookingGlass = class LookingGlass {
}
_pushResult(command, obj) {
let index = this._results.length + this._offset;
let index = this._resultsArea.get_n_children() + this._offset;
let result = new Result(this, CHEVRON + command, obj, index);
this._results.push(result);
this._resultsArea.add(result.actor);
this._resultsArea.add(result);
if (obj instanceof Clutter.Actor)
this.setBorderPaintTarget(obj);
let children = this._resultsArea.get_children();
if (children.length > this._maxItems) {
this._results.shift();
children[0].destroy();
if (this._resultsArea.get_n_children() > this._maxItems) {
this._resultsArea.get_first_child().destroy();
this._offset++;
}
this._it = obj;
@ -965,7 +1009,7 @@ var LookingGlass = class LookingGlass {
height: naturalHeight,
opacity: 255,
duration,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
}
@ -982,7 +1026,7 @@ var LookingGlass = class LookingGlass {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._completionActor.hide();
}
},
});
}
}
@ -1016,7 +1060,7 @@ var LookingGlass = class LookingGlass {
getResult(idx) {
try {
return this._results[idx - this._offset].o;
return this._resultsArea.get_child_at_index(idx - this._offset).o;
} catch (e) {
throw new Error(`Unknown result at index ${idx}`);
}
@ -1041,15 +1085,15 @@ var LookingGlass = class LookingGlass {
let myWidth = primary.width * 0.7;
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
this.actor.x = primary.x + (primary.width - myWidth) / 2;
this.x = primary.x + (primary.width - myWidth) / 2;
this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight;
this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY;
this.actor.width = myWidth;
this.actor.height = myHeight;
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
this._targetY + Math.floor(myHeight * 0.1));
this.y = this._hiddenY;
this.width = myWidth;
this.height = myHeight;
this._objInspector.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
this._objInspector.set_position(this.x + Math.floor(myWidth * 0.1),
this._targetY + Math.floor(myHeight * 0.1));
}
insertObject(obj) {
@ -1062,24 +1106,21 @@ var LookingGlass = class LookingGlass {
}
// Handle key events which are relevant for all tabs of the LookingGlass
_globalKeyPressEvent(actor, event) {
let symbol = event.get_key_symbol();
let modifierState = event.get_state();
if (symbol == Clutter.Escape) {
if (this._objInspector.actor.visible) {
vfunc_key_press_event(keyPressEvent) {
let symbol = keyPressEvent.keyval;
if (symbol == Clutter.KEY_Escape) {
if (this._objInspector.visible)
this._objInspector.close();
} else {
else
this.close();
}
return Clutter.EVENT_STOP;
}
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
if (symbol == Clutter.KEY_Page_Up) {
if (keyPressEvent.modifier_state & Clutter.ModifierType.CONTROL_MASK) {
if (symbol == Clutter.KEY_Page_Up)
this._notebook.prevTab();
} else if (symbol == Clutter.KEY_Page_Down) {
else if (symbol == Clutter.KEY_Page_Down)
this._notebook.nextTab();
}
}
return Clutter.EVENT_PROPAGATE;
}
@ -1092,19 +1133,19 @@ var LookingGlass = class LookingGlass {
return;
this._notebook.selectIndex(0);
this.actor.show();
this.show();
this._open = true;
this._history.lastItem();
this.actor.remove_all_transitions();
this.remove_all_transitions();
// We inverse compensate for the slow-down so you can change the factor
// through LookingGlass without long waits.
let duration = LG_ANIMATION_TIME / St.Settings.get().slow_down_factor;
this.actor.ease({
this.ease({
y: this._targetY,
duration,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
this._windowList.update();
@ -1114,10 +1155,10 @@ var LookingGlass = class LookingGlass {
if (!this._open)
return;
this._objInspector.actor.hide();
this._objInspector.hide();
this._open = false;
this.actor.remove_all_transitions();
this.remove_all_transitions();
this.setBorderPaintTarget(null);
@ -1126,16 +1167,15 @@ var LookingGlass = class LookingGlass {
let settings = St.Settings.get();
let duration = Math.min(LG_ANIMATION_TIME / settings.slow_down_factor,
LG_ANIMATION_TIME);
this.actor.ease({
this.ease({
y: this._hiddenY,
duration,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this.actor.hide()
onComplete: () => this.hide(),
});
}
get isOpen() {
return this._open;
}
};
Signals.addSignalMethods(LookingGlass.prototype);
});

View File

@ -55,7 +55,7 @@ var MouseSpriteContent = GObject.registerClass({
return [true, this._texture.get_width(), this._texture.get_height()];
}
vfunc_paint_content(actor, node) {
vfunc_paint_content(actor, node, _paintContext) {
if (!this._texture)
return;
@ -118,7 +118,7 @@ var Magnifier = class Magnifier {
});
// Export to dbus.
(new MagnifierDBus.ShellMagnifier());
new MagnifierDBus.ShellMagnifier();
this.setActive(St.Settings.get().magnifier_active);
}
@ -141,7 +141,7 @@ var Magnifier = class Magnifier {
/**
* setActive:
* Show/hide all the zoom regions.
* @activate: Boolean to activate or de-activate the magnifier.
* @param {bool} activate: Boolean to activate or de-activate the magnifier.
*/
setActive(activate) {
let isActive = this.isActive();
@ -177,7 +177,7 @@ var Magnifier = class Magnifier {
/**
* isActive:
* @return Whether the magnifier is active (boolean).
* @returns {bool} Whether the magnifier is active.
*/
isActive() {
// Sufficient to check one ZoomRegion since Magnifier's active
@ -212,7 +212,7 @@ var Magnifier = class Magnifier {
/**
* isTrackingMouse:
* Is the magnifier tracking the mouse currently?
* @returns {bool} whether the magnifier is currently tracking the mouse
*/
isTrackingMouse() {
return !!this._mouseTrackingId;
@ -222,7 +222,7 @@ var Magnifier = class Magnifier {
* scrollToMousePos:
* Position all zoom regions' ROI relative to the current location of the
* system pointer.
* @return true.
* @returns {bool} true.
*/
scrollToMousePos() {
let [xMouse, yMouse] = global.get_pointer();
@ -247,17 +247,17 @@ var Magnifier = class Magnifier {
/**
* createZoomRegion:
* Create a ZoomRegion instance with the given properties.
* @xMagFactor: The power to set horizontal magnification of the
* ZoomRegion. A value of 1.0 means no magnification. A
* value of 2.0 doubles the size.
* @yMagFactor: The power to set the vertical magnification of the
* ZoomRegion.
* @roi Object in the form { x, y, width, height } that
* defines the region to magnify. Given in unmagnified
* coordinates.
* @viewPort Object in the form { x, y, width, height } that defines
* the position of the ZoomRegion on screen.
* @return The newly created ZoomRegion.
* @param {number} xMagFactor:
* The power to set horizontal magnification of the ZoomRegion. A value
* of 1.0 means no magnification, a value of 2.0 doubles the size.
* @param {number} yMagFactor:
* The power to set the vertical magnification of the ZoomRegion.
* @param {{x: number, y: number, width: number, height: number}} roi:
* The reg Object that defines the region to magnify, given in
* unmagnified coordinates.
* @param {{x: number, y: number, width: number, height: number}} viewPort:
* Object that defines the position of the ZoomRegion on screen.
* @returns {ZoomRegion} the newly created ZoomRegion.
*/
createZoomRegion(xMagFactor, yMagFactor, roi, viewPort) {
let zoomRegion = new ZoomRegion(this, this._cursorRoot);
@ -277,7 +277,7 @@ var Magnifier = class Magnifier {
* addZoomRegion:
* Append the given ZoomRegion to the list of currently defined ZoomRegions
* for this Magnifier instance.
* @zoomRegion: The zoomRegion to add.
* @param {ZoomRegion} zoomRegion: The zoomRegion to add.
*/
addZoomRegion(zoomRegion) {
if (zoomRegion) {
@ -290,7 +290,7 @@ var Magnifier = class Magnifier {
/**
* getZoomRegions:
* Return a list of ZoomRegion's for this Magnifier.
* @return: The Magnifier's zoom region list (array).
* @returns {number[]} The Magnifier's zoom region list.
*/
getZoomRegions() {
return this._zoomRegions;
@ -330,7 +330,7 @@ var Magnifier = class Magnifier {
this.setCrosshairsClip(clip);
let theCrossHairs = this._crossHairs;
this._zoomRegions.forEach (zoomRegion => {
this._zoomRegions.forEach(zoomRegion => {
zoomRegion.addCrosshairs(theCrossHairs);
});
}
@ -338,7 +338,7 @@ var Magnifier = class Magnifier {
/**
* setCrosshairsVisible:
* Show or hide the cross hair.
* @visible Flag that indicates show (true) or hide (false).
* @param {bool} visible: Flag that indicates show (true) or hide (false).
*/
setCrosshairsVisible(visible) {
if (visible) {
@ -346,6 +346,7 @@ var Magnifier = class Magnifier {
this.addCrosshairs();
this._crossHairs.show();
} else {
// eslint-disable-next-line no-lonely-if
if (this._crossHairs)
this._crossHairs.hide();
}
@ -354,7 +355,7 @@ var Magnifier = class Magnifier {
/**
* setCrosshairsColor:
* Set the color of the crosshairs for all ZoomRegions.
* @color: The color as a string, e.g. '#ff0000ff' or 'red'.
* @param {string} color: The color as a string, e.g. '#ff0000ff' or 'red'.
*/
setCrosshairsColor(color) {
if (this._crossHairs) {
@ -366,7 +367,7 @@ var Magnifier = class Magnifier {
/**
* getCrosshairsColor:
* Get the color of the crosshairs.
* @return: The color as a string, e.g. '#0000ffff' or 'blue'.
* @returns {string} The color as a string, e.g. '#0000ffff' or 'blue'.
*/
getCrosshairsColor() {
if (this._crossHairs) {
@ -380,8 +381,8 @@ var Magnifier = class Magnifier {
/**
* setCrosshairsThickness:
* Set the crosshairs thickness for all ZoomRegions.
* @thickness: The width of the vertical and horizontal lines of the
* crosshairs.
* @param {number} thickness: The width of the vertical and
* horizontal lines of the crosshairs.
*/
setCrosshairsThickness(thickness) {
if (this._crossHairs)
@ -391,8 +392,8 @@ var Magnifier = class Magnifier {
/**
* getCrosshairsThickness:
* Get the crosshairs thickness.
* @return: The width of the vertical and horizontal lines of the
* crosshairs.
* @returns {number} The width of the vertical and horizontal
* lines of the crosshairs.
*/
getCrosshairsThickness() {
if (this._crossHairs)
@ -403,7 +404,8 @@ var Magnifier = class Magnifier {
/**
* setCrosshairsOpacity:
* @opacity: Value between 0.0 (transparent) and 1.0 (fully opaque).
* @param {number} opacity: Value between 0.0 (transparent)
* and 1.0 (fully opaque).
*/
setCrosshairsOpacity(opacity) {
if (this._crossHairs)
@ -412,7 +414,7 @@ var Magnifier = class Magnifier {
/**
* getCrosshairsOpacity:
* @return: Value between 0.0 (transparent) and 1.0 (fully opaque).
* @returns {number} Value between 0.0 (transparent) and 1.0 (fully opaque).
*/
getCrosshairsOpacity() {
if (this._crossHairs)
@ -424,8 +426,8 @@ var Magnifier = class Magnifier {
/**
* setCrosshairsLength:
* Set the crosshairs length for all ZoomRegions.
* @length: The length of the vertical and horizontal lines making up the
* crosshairs.
* @param {number} length: The length of the vertical and horizontal
* lines making up the crosshairs.
*/
setCrosshairsLength(length) {
if (this._crossHairs)
@ -435,8 +437,8 @@ var Magnifier = class Magnifier {
/**
* getCrosshairsLength:
* Get the crosshairs length.
* @return: The length of the vertical and horizontal lines making up the
* crosshairs.
* @returns {number} The length of the vertical and horizontal
* lines making up the crosshairs.
*/
getCrosshairsLength() {
if (this._crossHairs)
@ -448,7 +450,7 @@ var Magnifier = class Magnifier {
/**
* setCrosshairsClip:
* Set whether the crosshairs are clipped at their intersection.
* @clip: Flag to indicate whether to clip the crosshairs.
* @param {bool} clip: Flag to indicate whether to clip the crosshairs.
*/
setCrosshairsClip(clip) {
if (!this._crossHairs)
@ -461,18 +463,18 @@ var Magnifier = class Magnifier {
/**
* getCrosshairsClip:
* Get whether the crosshairs are clipped by the mouse image.
* @return: Whether the crosshairs are clipped.
* @returns {bool} Whether the crosshairs are clipped.
*/
getCrosshairsClip() {
if (this._crossHairs) {
let [clipWidth, clipHeight] = this._crossHairs.getClip();
return (clipWidth > 0 && clipHeight > 0);
return clipWidth > 0 && clipHeight > 0;
} else {
return false;
}
}
//// Private methods ////
// Private methods //
_updateMouseSprite() {
this._updateSpriteTexture();
@ -623,9 +625,8 @@ var Magnifier = class Magnifier {
_updateLensMode() {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
if (this._zoomRegions.length)
this._zoomRegions[0].setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
}
}
_updateClampMode() {
@ -772,15 +773,16 @@ var ZoomRegion = class ZoomRegion {
}
_updateScreenPosition() {
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE) {
this._setViewPort({
x: this._viewPortX,
y: this._viewPortY,
width: this._viewPortWidth,
height: this._viewPortHeight
height: this._viewPortHeight,
});
else
} else {
this.setScreenPosition(this._screenPosition);
}
}
_updateFocus(caller, event) {
@ -818,7 +820,7 @@ var ZoomRegion = class ZoomRegion {
/**
* setActive:
* @activate: Boolean to show/hide the ZoomRegion.
* @param {bool} activate: Boolean to show/hide the ZoomRegion.
*/
setActive(activate) {
if (activate == this.isActive())
@ -844,7 +846,7 @@ var ZoomRegion = class ZoomRegion {
/**
* isActive:
* @return Whether this ZoomRegion is active (boolean).
* @returns {bool} Whether this ZoomRegion is active
*/
isActive() {
return this._magView != null;
@ -852,24 +854,24 @@ var ZoomRegion = class ZoomRegion {
/**
* setMagFactor:
* @xMagFactor: The power to set the horizontal magnification factor to
* of the magnified view. A value of 1.0 means no
* magnification. A value of 2.0 doubles the size.
* @yMagFactor: The power to set the vertical magnification factor to
* of the magnified view.
* @param {number} xMagFactor: The power to set the horizontal
* magnification factor to of the magnified view. A value of 1.0
* means no magnification. A value of 2.0 doubles the size.
* @param {number} yMagFactor: The power to set the vertical
* magnification factor to of the magnified view.
*/
setMagFactor(xMagFactor, yMagFactor) {
this._changeROI({ xMagFactor: xMagFactor,
yMagFactor: yMagFactor,
this._changeROI({ xMagFactor,
yMagFactor,
redoCursorTracking: this._followingCursor });
}
/**
* getMagFactor:
* @return an array, [xMagFactor, yMagFactor], containing the horizontal
* and vertical magnification powers. A value of 1.0 means no
* magnification. A value of 2.0 means the contents are doubled
* in size, and so on.
* @returns {number[]} an array, [xMagFactor, yMagFactor], containing
* the horizontal and vertical magnification powers. A value of
* 1.0 means no magnification. A value of 2.0 means the contents
* are doubled in size, and so on.
*/
getMagFactor() {
return [this._xMagFactor, this._yMagFactor];
@ -877,7 +879,7 @@ var ZoomRegion = class ZoomRegion {
/**
* setMouseTrackingMode
* @mode: One of the enum MouseTrackingMode values.
* @param {GDesktopEnums.MagnifierMouseTrackingMode} mode: the new mode
*/
setMouseTrackingMode(mode) {
if (mode >= GDesktopEnums.MagnifierMouseTrackingMode.NONE &&
@ -887,7 +889,7 @@ var ZoomRegion = class ZoomRegion {
/**
* getMouseTrackingMode
* @return: One of the enum MouseTrackingMode values.
* @returns {GDesktopEnums.MagnifierMouseTrackingMode} the current mode
*/
getMouseTrackingMode() {
return this._mouseTrackingMode;
@ -895,7 +897,7 @@ var ZoomRegion = class ZoomRegion {
/**
* setFocusTrackingMode
* @mode: One of the enum FocusTrackingMode values.
* @param {GDesktopEnums.MagnifierFocusTrackingMode} mode: the new mode
*/
setFocusTrackingMode(mode) {
this._focusTrackingMode = mode;
@ -904,7 +906,7 @@ var ZoomRegion = class ZoomRegion {
/**
* setCaretTrackingMode
* @mode: One of the enum CaretTrackingMode values.
* @param {GDesktopEnums.MagnifierCaretTrackingMode} mode: the new mode
*/
setCaretTrackingMode(mode) {
this._caretTrackingMode = mode;
@ -934,9 +936,9 @@ var ZoomRegion = class ZoomRegion {
/**
* setViewPort
* Sets the position and size of the ZoomRegion on screen.
* @viewPort: Object defining the position and size of the view port.
* It has members x, y, width, height. The values are in
* stage coordinate space.
* @param {{x: number, y: number, width: number, height: number}} viewPort:
* Object defining the position and size of the view port.
* The values are in stage coordinate space.
*/
setViewPort(viewPort) {
this._setViewPort(viewPort);
@ -946,9 +948,9 @@ var ZoomRegion = class ZoomRegion {
/**
* setROI
* Sets the "region of interest" that the ZoomRegion is magnifying.
* @roi: Object that defines the region of the screen to magnify. It
* has members x, y, width, height. The values are in
* screen (unmagnified) coordinate space.
* @param {{x: number, y: number, width: number, height: number}} roi:
* Object that defines the region of the screen to magnify.
* The values are in screen (unmagnified) coordinate space.
*/
setROI(roi) {
if (roi.width <= 0 || roi.height <= 0)
@ -966,8 +968,8 @@ var ZoomRegion = class ZoomRegion {
* Retrieves the "region of interest" -- the rectangular bounds of that part
* of the desktop that the magnified view is showing (x, y, width, height).
* The bounds are given in non-magnified coordinates.
* @return an array, [x, y, width, height], representing the bounding
* rectangle of what is shown in the magnified view.
* @returns {number[]} an array, [x, y, width, height], representing
* the bounding rectangle of what is shown in the magnified view.
*/
getROI() {
let roiWidth = this._viewPortWidth / this._xMagFactor;
@ -982,18 +984,18 @@ var ZoomRegion = class ZoomRegion {
* setLensMode:
* Turn lens mode on/off. In full screen mode, lens mode does nothing since
* a lens the size of the screen is pointless.
* @lensMode: A boolean to set the sense of lens mode.
* @param {bool} lensMode: Whether lensMode should be active
*/
setLensMode(lensMode) {
this._lensMode = lensMode;
if (!this._lensMode)
this.setScreenPosition (this._screenPosition);
this.setScreenPosition(this._screenPosition);
}
/**
* isLensMode:
* Is lens mode on or off?
* @return The lens mode state as a boolean.
* @returns {bool} The lens mode state.
*/
isLensMode() {
return this._lensMode;
@ -1003,7 +1005,7 @@ var ZoomRegion = class ZoomRegion {
* setClampScrollingAtEdges:
* Stop vs. allow scrolling of the magnified contents when it scroll beyond
* the edges of the screen.
* @clamp: Boolean to turn on/off clamping.
* @param {bool} clamp: Boolean to turn on/off clamping.
*/
setClampScrollingAtEdges(clamp) {
this._clampScrollingAtEdges = clamp;
@ -1087,9 +1089,7 @@ var ZoomRegion = class ZoomRegion {
* setScreenPosition:
* Positions the zoom region to one of the enumerated positions on the
* screen.
* @position: one of Magnifier.FULL_SCREEN, Magnifier.TOP_HALF,
* Magnifier.BOTTOM_HALF,Magnifier.LEFT_HALF, or
* Magnifier.RIGHT_HALF.
* @param {GDesktopEnums.MagnifierScreenPosition} inPosition: the position
*/
setScreenPosition(inPosition) {
switch (inPosition) {
@ -1115,7 +1115,7 @@ var ZoomRegion = class ZoomRegion {
* getScreenPosition:
* Tell the outside world what the current mode is -- magnifiying the
* top half, bottom half, etc.
* @return: the current mode.
* @returns {GDesktopEnums.MagnifierScreenPosition}: the current position.
*/
getScreenPosition() {
return this._screenPosition;
@ -1124,7 +1124,8 @@ var ZoomRegion = class ZoomRegion {
/**
* scrollToMousePos:
* Set the region of interest based on the position of the system pointer.
* @return: Whether the system mouse pointer is over the magnified view.
* @returns {bool}: Whether the system mouse pointer is over the
* magnified view.
*/
scrollToMousePos() {
this._followingCursor = true;
@ -1161,8 +1162,8 @@ var ZoomRegion = class ZoomRegion {
* scrollContentsTo:
* Shift the contents of the magnified view such it is centered on the given
* coordinate.
* @x: The x-coord of the point to center on.
* @y: The y-coord of the point to center on.
* @param {number} x: The x-coord of the point to center on.
* @param {number} y: The y-coord of the point to center on.
*/
scrollContentsTo(x, y) {
this._clearScrollContentsTimer();
@ -1175,22 +1176,21 @@ var ZoomRegion = class ZoomRegion {
/**
* addCrosshairs:
* Add crosshairs centered on the magnified mouse.
* @crossHairs: Crosshairs instance
* @param {Crosshairs} crossHairs: Crosshairs instance
*/
addCrosshairs(crossHairs) {
this._crossHairs = crossHairs;
// If the crossHairs is not already within a larger container, add it
// to this zoom region. Otherwise, add a clone.
if (crossHairs && this.isActive()) {
if (crossHairs && this.isActive())
this._crossHairsActor = crossHairs.addToZoomRegion(this, this._mouseActor);
}
}
/**
* setInvertLightness:
* Set whether to invert the lightness of the magnified view.
* @flag Boolean to either invert brightness (true), or not (false).
* @param {bool} flag: whether brightness should be inverted
*/
setInvertLightness(flag) {
this._invertLightness = flag;
@ -1201,7 +1201,7 @@ var ZoomRegion = class ZoomRegion {
/**
* getInvertLightness:
* Retrieve whether the lightness is inverted.
* @return Boolean indicating inversion (true), or not (false).
* @returns {bool} whether brightness should be inverted
*/
getInvertLightness() {
return this._invertLightness;
@ -1210,9 +1210,9 @@ var ZoomRegion = class ZoomRegion {
/**
* setColorSaturation:
* Set the color saturation of the magnified view.
* @sauration A value from 0.0 to 1.0 that defines the color
* saturation, with 0.0 defining no color (grayscale),
* and 1.0 defining full color.
* @param {number} saturation: A value from 0.0 to 1.0 that defines
* the color saturation, with 0.0 defining no color (grayscale),
* and 1.0 defining full color.
*/
setColorSaturation(saturation) {
this._colorSaturation = saturation;
@ -1223,6 +1223,7 @@ var ZoomRegion = class ZoomRegion {
/**
* getColorSaturation:
* Retrieve the color saturation of the magnified view.
* @returns {number} the color saturation
*/
getColorSaturation() {
return this._colorSaturation;
@ -1231,10 +1232,14 @@ var ZoomRegion = class ZoomRegion {
/**
* setBrightness:
* Alter the brightness of the magnified view.
* @brightness Object containing the contrast for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* brightness (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed brightness, respectively.
* @param {Object} brightness: Object containing the contrast for the
* red, green, and blue channels. Values of 0.0 represent "standard"
* brightness (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed brightness, respectively.
*
* {number} brightness.r - the red component
* {number} brightness.g - the green component
* {number} brightness.b - the blue component
*/
setBrightness(brightness) {
this._brightness.r = brightness.r;
@ -1247,10 +1252,14 @@ var ZoomRegion = class ZoomRegion {
/**
* setContrast:
* Alter the contrast of the magnified view.
* @contrast Object containing the contrast for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* contrast (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed contrast, respectively.
* @param {Object} contrast: Object containing the contrast for the
* red, green, and blue channels. Values of 0.0 represent "standard"
* contrast (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed contrast, respectively.
*
* {number} contrast.r - the red component
* {number} contrast.g - the green component
* {number} contrast.b - the blue component
*/
setContrast(contrast) {
this._contrast.r = contrast.r;
@ -1263,8 +1272,8 @@ var ZoomRegion = class ZoomRegion {
/**
* getContrast:
* Retrieve the contrast of the magnified view.
* @return Object containing the contrast for the red, green,
* and blue channels.
* @returns {{r: number, g: number, b: number}}: Object containing
* the contrast for the red, green, and blue channels.
*/
getContrast() {
let contrast = {};
@ -1274,15 +1283,15 @@ var ZoomRegion = class ZoomRegion {
return contrast;
}
//// Private methods ////
// Private methods //
_createActors() {
// The root actor for the zoom region
this._magView = new St.Bin({ style_class: 'magnifier-zoom-region', x_fill: true, y_fill: true });
this._magView = new St.Bin({ style_class: 'magnifier-zoom-region' });
global.stage.add_actor(this._magView);
// hide the magnified region from CLUTTER_PICK_ALL
Shell.util_set_hidden_from_pick (this._magView, true);
Shell.util_set_hidden_from_pick(this._magView, true);
// Add a group to clip the contents of the magnified view.
let mainGroup = new Clutter.Actor({ clip_to_allocation: true });
@ -1290,7 +1299,7 @@ var ZoomRegion = class ZoomRegion {
// Add a background for when the magnified uiGroup is scrolled
// out of view (don't want to see desktop showing through).
this._background = (new Background.SystemBackground()).actor;
this._background = new Background.SystemBackground();
mainGroup.add_actor(this._background);
// Clone the group that contains all of UI on the screen. This is the
@ -1322,7 +1331,7 @@ var ZoomRegion = class ZoomRegion {
_destroyActors() {
if (this._mouseActor == this._mouseSourceActor)
this._mouseActor.get_parent().remove_actor (this._mouseActor);
this._mouseActor.get_parent().remove_actor(this._mouseActor);
if (this._crossHairs)
this._crossHairs.removeFromParent(this._crossHairsActor);
@ -1402,11 +1411,12 @@ var ZoomRegion = class ZoomRegion {
// If in lens mode, move the magnified view such that it is centered
// over the actual mouse. However, in full screen mode, the "lens" is
// the size of the screen -- pointless to move such a large lens around.
if (this._lensMode && !this._isFullScreen())
if (this._lensMode && !this._isFullScreen()) {
this._setViewPort({ x: this._xCenter - this._viewPortWidth / 2,
y: this._yCenter - this._viewPortHeight / 2,
width: this._viewPortWidth,
height: this._viewPortHeight }, true);
}
this._updateCloneGeometry();
this._updateMousePosition();
@ -1420,10 +1430,9 @@ var ZoomRegion = class ZoomRegion {
let xMouse = this._magnifier.xMouse;
let yMouse = this._magnifier.yMouse;
mouseIsOver = (
mouseIsOver =
xMouse >= this._viewPortX && xMouse < (this._viewPortX + this._viewPortWidth) &&
yMouse >= this._viewPortY && yMouse < (this._viewPortY + this._viewPortHeight)
);
yMouse >= this._viewPortY && yMouse < (this._viewPortY + this._viewPortHeight);
}
return mouseIsOver;
}
@ -1448,13 +1457,12 @@ var ZoomRegion = class ZoomRegion {
let xMouse = this._magnifier.xMouse;
let yMouse = this._magnifier.yMouse;
if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL)
return this._centerFromPointProportional(xMouse, yMouse);
} else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH)
return this._centerFromPointPush(xMouse, yMouse);
} else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED)
return this._centerFromPointCentered(xMouse, yMouse);
}
return null; // Should never be hit
}
@ -1496,14 +1504,14 @@ var ZoomRegion = class ZoomRegion {
let yRoiBottom = yRoi + heightRoi - cursorHeight;
if (xPoint < xRoi)
xPos -= (xRoi - xPoint);
xPos -= xRoi - xPoint;
else if (xPoint > xRoiRight)
xPos += (xPoint - xRoiRight);
xPos += xPoint - xRoiRight;
if (yPoint < yRoi)
yPos -= (yRoi - yPoint);
yPos -= yRoi - yPoint;
else if (yPoint > yRoiBottom)
yPos += (yPoint - yRoiBottom);
yPos += yPoint - yRoiBottom;
return [xPos, yPos];
}
@ -1587,8 +1595,9 @@ var ZoomRegion = class ZoomRegion {
}
};
var Crosshairs = class Crosshairs {
constructor() {
var Crosshairs = GObject.registerClass(
class Crosshairs extends Clutter.Actor {
_init() {
// Set the group containing the crosshairs to three times the desktop
// size in case the crosshairs need to appear to be infinite in
@ -1596,19 +1605,19 @@ var Crosshairs = class Crosshairs {
let groupWidth = global.screen_width * 3;
let groupHeight = global.screen_height * 3;
this._actor = new Clutter.Actor({
super._init({
clip_to_allocation: false,
width: groupWidth,
height: groupHeight
height: groupHeight,
});
this._horizLeftHair = new Clutter.Actor();
this._horizRightHair = new Clutter.Actor();
this._vertTopHair = new Clutter.Actor();
this._vertBottomHair = new Clutter.Actor();
this._actor.add_actor(this._horizLeftHair);
this._actor.add_actor(this._horizRightHair);
this._actor.add_actor(this._vertTopHair);
this._actor.add_actor(this._vertBottomHair);
this.add_actor(this._horizLeftHair);
this.add_actor(this._horizRightHair);
this.add_actor(this._vertTopHair);
this.add_actor(this._vertBottomHair);
this._clipSize = [0, 0];
this._clones = [];
this.reCenter();
@ -1618,7 +1627,7 @@ var Crosshairs = class Crosshairs {
}
_monitorsChanged() {
this._actor.set_size(global.screen_width * 3, global.screen_height * 3);
this.set_size(global.screen_width * 3, global.screen_height * 3);
this.reCenter();
}
@ -1628,26 +1637,30 @@ var Crosshairs = class Crosshairs {
* already part of some other ZoomRegion, create a clone of the crosshairs
* actor, and add the clone instead. Returns either the original or the
* clone.
* @zoomRegion: The container to add the crosshairs group to.
* @magnifiedMouse: The mouse actor for the zoom region -- used to
* position the crosshairs and properly layer them below
* the mouse.
* @return The crosshairs actor, or its clone.
* @param {ZoomRegion} zoomRegion: The container to add the crosshairs
* group to.
* @param {Clutter.Actor} magnifiedMouse: The mouse actor for the
* zoom region -- used to position the crosshairs and properly
* layer them below the mouse.
* @returns {Clutter.Actor} The crosshairs actor, or its clone.
*/
addToZoomRegion(zoomRegion, magnifiedMouse) {
let crosshairsActor = null;
if (zoomRegion && magnifiedMouse) {
let container = magnifiedMouse.get_parent();
if (container) {
crosshairsActor = this._actor;
if (this._actor.get_parent() != null) {
crosshairsActor = new Clutter.Clone({ source: this._actor });
crosshairsActor = this;
if (this.get_parent() != null) {
crosshairsActor = new Clutter.Clone({ source: this });
this._clones.push(crosshairsActor);
// Clones don't share visibility.
this.bind_property('visible', crosshairsActor, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
crosshairsActor.visible = this._actor.visible;
container.add_actor(crosshairsActor);
container.raise_child(magnifiedMouse, crosshairsActor);
container.set_child_above_sibling(magnifiedMouse, crosshairsActor);
let [xMouse, yMouse] = magnifiedMouse.get_position();
let [crosshairsWidth, crosshairsHeight] = crosshairsActor.get_size();
crosshairsActor.set_position(xMouse - crosshairsWidth / 2, yMouse - crosshairsHeight / 2);
@ -1658,12 +1671,13 @@ var Crosshairs = class Crosshairs {
/**
* removeFromParent:
* @childActor: the actor returned from addToZoomRegion
* @param {Clutter.Actor} childActor: the actor returned from
* addToZoomRegion
* Remove the crosshairs actor from its parent container, or destroy the
* child actor if it was just a clone of the crosshairs actor.
*/
removeFromParent(childActor) {
if (childActor == this._actor)
if (childActor == this)
childActor.get_parent().remove_actor(childActor);
else
childActor.destroy();
@ -1672,7 +1686,7 @@ var Crosshairs = class Crosshairs {
/**
* setColor:
* Set the color of the crosshairs.
* @clutterColor: The color as a Clutter.Color.
* @param {Clutter.Color} clutterColor: The color
*/
setColor(clutterColor) {
this._horizLeftHair.background_color = clutterColor;
@ -1684,7 +1698,7 @@ var Crosshairs = class Crosshairs {
/**
* getColor:
* Get the color of the crosshairs.
* @color: The color as a Clutter.Color.
* @returns {ClutterColor} the crosshairs color
*/
getColor() {
return this._horizLeftHair.get_color();
@ -1693,7 +1707,7 @@ var Crosshairs = class Crosshairs {
/**
* setThickness:
* Set the width of the vertical and horizontal lines of the crosshairs.
* @thickness
* @param {number} thickness: the new thickness value
*/
setThickness(thickness) {
this._horizLeftHair.set_height(thickness);
@ -1706,7 +1720,7 @@ var Crosshairs = class Crosshairs {
/**
* getThickness:
* Get the width of the vertical and horizontal lines of the crosshairs.
* @return: The thickness of the crosshairs.
* @returns {number} The thickness of the crosshairs.
*/
getThickness() {
return this._horizLeftHair.get_height();
@ -1715,7 +1729,8 @@ var Crosshairs = class Crosshairs {
/**
* setOpacity:
* Set how opaque the crosshairs are.
* @opacity: Value between 0 (fully transparent) and 255 (full opaque).
* @param {number} opacity: Value between 0 (fully transparent)
* and 255 (full opaque).
*/
setOpacity(opacity) {
// set_opacity() throws an exception for values outside the range
@ -1734,7 +1749,7 @@ var Crosshairs = class Crosshairs {
/**
* setLength:
* Set the length of the vertical and horizontal lines in the crosshairs.
* @length: The length of the crosshairs.
* @param {number} length: The length of the crosshairs.
*/
setLength(length) {
this._horizLeftHair.set_width(length);
@ -1747,7 +1762,7 @@ var Crosshairs = class Crosshairs {
/**
* getLength:
* Get the length of the vertical and horizontal lines in the crosshairs.
* @return: The length of the crosshairs.
* @returns {number} The length of the crosshairs.
*/
getLength() {
return this._horizLeftHair.get_width();
@ -1757,8 +1772,8 @@ var Crosshairs = class Crosshairs {
* setClip:
* Set the width and height of the rectangle that clips the crosshairs at
* their intersection
* @size: Array of [width, height] defining the size of the clip
* rectangle.
* @param {number[]} size: Array of [width, height] defining the size
* of the clip rectangle.
*/
setClip(size) {
if (size) {
@ -1773,37 +1788,15 @@ var Crosshairs = class Crosshairs {
}
}
/**
* show:
* Show the crosshairs.
*/
show() {
this._actor.show();
// Clones don't share visibility.
for (let i = 0; i < this._clones.length; i++)
this._clones[i].show();
}
/**
* hide:
* Hide the crosshairs.
*/
hide() {
this._actor.hide();
// Clones don't share visibility.
for (let i = 0; i < this._clones.length; i++)
this._clones[i].hide();
}
/**
* reCenter:
* Reposition the horizontal and vertical hairs such that they cross at
* the center of crosshairs group. If called with the dimensions of
* the clip rectangle, these are used to update the size of the clip.
* @clipSize: Optional. If present, an array of the form [width, height].
* @param {number[]=} clipSize: If present, the clip's [width, height].
*/
reCenter(clipSize) {
let [groupWidth, groupHeight] = this._actor.get_size();
let [groupWidth, groupHeight] = this.get_size();
let leftLength = this._horizLeftHair.get_width();
let topLength = this._vertTopHair.get_height();
let thickness = this._horizLeftHair.get_height();
@ -1825,7 +1818,7 @@ var Crosshairs = class Crosshairs {
this._vertTopHair.set_position((groupWidth - thickness) / 2, top);
this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
}
};
});
var MagShaderEffects = class MagShaderEffects {
constructor(uiGroupClone) {
@ -1858,7 +1851,7 @@ var MagShaderEffects = class MagShaderEffects {
/**
* setInvertLightness:
* Enable/disable invert lightness effect.
* @invertFlag: Enabled flag.
* @param {bool} invertFlag: Enabled flag.
*/
setInvertLightness(invertFlag) {
this._inverse.set_enabled(invertFlag);
@ -1871,11 +1864,14 @@ var MagShaderEffects = class MagShaderEffects {
/**
* setBrightness:
* Set the brightness of the magnified view.
* @brightness: Object containing the brightness for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* brightness (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed brightness,
* respectively.
* @param {Object} brightness: Object containing the contrast for the
* red, green, and blue channels. Values of 0.0 represent "standard"
* brightness (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed brightness, respectively.
*
* {number} brightness.r - the red component
* {number} brightness.g - the green component
* {number} brightness.b - the blue component
*/
setBrightness(brightness) {
let bRed = brightness.r;
@ -1887,17 +1883,21 @@ var MagShaderEffects = class MagShaderEffects {
// it modifies the brightness and/or contrast.
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
this._brightnessContrast.set_enabled(
(bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE ||
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE)
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE ||
cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE
);
}
/**
* Set the contrast of the magnified view.
* @contrast: Object containing the contrast for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* contrast (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed contrast, respectively.
* @param {Object} contrast: Object containing the contrast for the
* red, green, and blue channels. Values of 0.0 represent "standard"
* contrast (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed contrast, respectively.
*
* {number} contrast.r - the red component
* {number} contrast.g - the green component
* {number} contrast.b - the blue component
*/
setContrast(contrast) {
let cRed = contrast.r;

View File

@ -32,7 +32,7 @@ var ShellMagnifier = class ShellMagnifier {
/**
* setActive:
* @activate: Boolean to activate or de-activate the magnifier.
* @param {bool} activate: activate or de-activate the magnifier.
*/
setActive(activate) {
Main.magnifier.setActive(activate);
@ -40,7 +40,7 @@ var ShellMagnifier = class ShellMagnifier {
/**
* isActive:
* @return Whether the magnifier is active (boolean).
* @returns {bool} Whether the magnifier is active.
*/
isActive() {
return Main.magnifier.isActive();
@ -65,22 +65,25 @@ var ShellMagnifier = class ShellMagnifier {
/**
* createZoomRegion:
* Create a new ZoomRegion and return its object path.
* @xMagFactor: The power to set horizontal magnification of the
* ZoomRegion. A value of 1.0 means no magnification. A
* value of 2.0 doubles the size.
* @yMagFactor: The power to set the vertical magnification of the
* ZoomRegion.
* @roi Array of integers defining the region of the
* screen/desktop to magnify. The array has the form
* [left, top, right, bottom].
* @viewPort Array of integers, [left, top, right, bottom] that defines
* the position of the ZoomRegion on screen.
* @param {number} xMagFactor:
* The power to set horizontal magnification of the ZoomRegion.
* A value of 1.0 means no magnification. A value of 2.0 doubles
* the size.
* @param {number} yMagFactor:
* The power to set the vertical magnification of the
* ZoomRegion.
* @param {number[]} roi
* Array of integers defining the region of the screen/desktop
* to magnify. The array has the form [left, top, right, bottom].
* @param {number[]} viewPort
* Array of integers, [left, top, right, bottom] that defines
* the position of the ZoomRegion on screen.
*
* FIXME: The arguments here are redundant, since the width and height of
* the ROI are determined by the viewport and magnification factors.
* We ignore the passed in width and height.
*
* @return The newly created ZoomRegion.
* @returns {ZoomRegion} The newly created ZoomRegion.
*/
createZoomRegion(xMagFactor, yMagFactor, roi, viewPort) {
let ROI = { x: roi[0], y: roi[1], width: roi[2] - roi[0], height: roi[3] - roi[1] };
@ -100,7 +103,9 @@ var ShellMagnifier = class ShellMagnifier {
/**
* addZoomRegion:
* Append the given ZoomRegion to the magnifier's list of ZoomRegions.
* @zoomerObjectPath: The object path for the zoom region proxy.
* @param {string} zoomerObjectPath: The object path for the zoom
* region proxy.
* @returns {bool} whether the region was added successfully
*/
addZoomRegion(zoomerObjectPath) {
let proxyAndZoomRegion = this._zoomers[zoomerObjectPath];
@ -115,8 +120,8 @@ var ShellMagnifier = class ShellMagnifier {
/**
* getZoomRegions:
* Return a list of ZoomRegion object paths for this Magnifier.
* @return: The Magnifier's zoom region list as an array of DBus object
* paths.
* @returns {string[]}: The Magnifier's zoom region list as an array
* of DBus object paths.
*/
getZoomRegions() {
// There may be more ZoomRegions in the magnifier itself than have
@ -125,7 +130,7 @@ var ShellMagnifier = class ShellMagnifier {
let zoomRegions = Main.magnifier.getZoomRegions();
let objectPaths = [];
let thoseZoomers = this._zoomers;
zoomRegions.forEach (aZoomRegion => {
zoomRegions.forEach(aZoomRegion => {
let found = false;
for (let objectPath in thoseZoomers) {
let proxyAndZoomRegion = thoseZoomers[objectPath];
@ -137,7 +142,7 @@ var ShellMagnifier = class ShellMagnifier {
}
if (!found) {
// Got a ZoomRegion with no DBus proxy, make one.
let newPath = ZOOM_SERVICE_PATH + '/zoomer' + _zoomRegionInstanceCount;
let newPath = `${ZOOM_SERVICE_PATH}/zoomer${_zoomRegionInstanceCount}`;
_zoomRegionInstanceCount++;
let zoomRegionProxy = new ShellMagnifierZoomRegion(newPath, aZoomRegion);
let proxyAndZoomer = {};
@ -169,7 +174,7 @@ var ShellMagnifier = class ShellMagnifier {
/**
* fullScreenCapable:
* Consult if the Magnifier can magnify in full-screen mode.
* @return Always return true.
* @returns {bool} Always return true.
*/
fullScreenCapable() {
return true;
@ -178,7 +183,7 @@ var ShellMagnifier = class ShellMagnifier {
/**
* setCrosswireSize:
* Set the crosswire size of all ZoomRegions.
* @size: The thickness of each line in the cross wire.
* @param {number} size: The thickness of each line in the cross wire.
*/
setCrosswireSize(size) {
Main.magnifier.setCrosshairsThickness(size);
@ -187,7 +192,7 @@ var ShellMagnifier = class ShellMagnifier {
/**
* getCrosswireSize:
* Get the crosswire size of all ZoomRegions.
* @return: The thickness of each line in the cross wire.
* @returns {number}: The thickness of each line in the cross wire.
*/
getCrosswireSize() {
return Main.magnifier.getCrosshairsThickness();
@ -196,16 +201,16 @@ var ShellMagnifier = class ShellMagnifier {
/**
* setCrosswireLength:
* Set the crosswire length of all zoom-regions..
* @size: The length of each line in the cross wire.
* @param {number} length: The length of each line in the cross wire.
*/
setCrosswireLength(length) {
Main.magnifier.setCrosshairsLength(length);
}
/**
* setCrosswireSize:
* Set the crosswire size of all zoom-regions.
* @size: The thickness of each line in the cross wire.
* getCrosswireSize:
* Get the crosswire length of all zoom-regions.
* @returns {number} size: The length of each line in the cross wire.
*/
getCrosswireLength() {
return Main.magnifier.getCrosshairsLength();
@ -214,7 +219,7 @@ var ShellMagnifier = class ShellMagnifier {
/**
* setCrosswireClip:
* Set if the crosswire will be clipped by the cursor image..
* @clip: Flag to indicate whether to clip the crosswire.
* @param {bool} clip: Flag to indicate whether to clip the crosswire.
*/
setCrosswireClip(clip) {
Main.magnifier.setCrosshairsClip(clip);
@ -223,7 +228,7 @@ var ShellMagnifier = class ShellMagnifier {
/**
* getCrosswireClip:
* Get the crosswire clip value.
* @return: Whether the crosswire is clipped by the cursor image.
* @returns {bool}: Whether the crosswire is clipped by the cursor image.
*/
getCrosswireClip() {
return Main.magnifier.getCrosshairsClip();
@ -232,7 +237,7 @@ var ShellMagnifier = class ShellMagnifier {
/**
* setCrosswireColor:
* Set the crosswire color of all ZoomRegions.
* @color: Unsigned int of the form rrggbbaa.
* @param {number} color: Unsigned int of the form rrggbbaa.
*/
setCrosswireColor(color) {
Main.magnifier.setCrosshairsColor('#%08x'.format(color));
@ -241,7 +246,8 @@ var ShellMagnifier = class ShellMagnifier {
/**
* getCrosswireClip:
* Get the crosswire color of all ZoomRegions.
* @return: The crosswire color as an unsigned int in the form rrggbbaa.
* @returns {number}: The crosswire color as an unsigned int in
* the form rrggbbaa.
*/
getCrosswireColor() {
let colorString = Main.magnifier.getCrosshairsColor();
@ -266,11 +272,11 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion {
/**
* setMagFactor:
* @xMagFactor: The power to set the horizontal magnification factor to
* of the magnified view. A value of 1.0 means no
* magnification. A value of 2.0 doubles the size.
* @yMagFactor: The power to set the vertical magnification factor to
* of the magnified view.
* @param {number} xMagFactor: The power to set the horizontal
* magnification factor to of the magnified view. A value of
* 1.0 means no magnification. A value of 2.0 doubles the size.
* @param {number} yMagFactor: The power to set the vertical
* magnification factor to of the magnified view.
*/
setMagFactor(xMagFactor, yMagFactor) {
this._zoomRegion.setMagFactor(xMagFactor, yMagFactor);
@ -278,7 +284,7 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion {
/**
* getMagFactor:
* @return an array, [xMagFactor, yMagFactor], containing the horizontal
* @returns {number[]}: [xMagFactor, yMagFactor], containing the horizontal
* and vertical magnification powers. A value of 1.0 means no
* magnification. A value of 2.0 means the contents are doubled
* in size, and so on.
@ -290,9 +296,9 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion {
/**
* setRoi:
* Sets the "region of interest" that the ZoomRegion is magnifying.
* @roi Array, [left, top, right, bottom], defining the region of the
* screen to magnify. The values are in screen (unmagnified)
* coordinate space.
* @param {number[]} roi: [left, top, right, bottom], defining the
* region of the screen to magnify.
* The values are in screen (unmagnified) coordinate space.
*/
setRoi(roi) {
let roiObject = { x: roi[0], y: roi[1], width: roi[2] - roi[0], height: roi[3] - roi[1] };
@ -304,7 +310,7 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion {
* Retrieves the "region of interest" -- the rectangular bounds of that part
* of the desktop that the magnified view is showing (x, y, width, height).
* The bounds are given in non-magnified coordinates.
* @return an array, [left, top, right, bottom], representing the bounding
* @returns {Array}: [left, top, right, bottom], representing the bounding
* rectangle of what is shown in the magnified view.
*/
getRoi() {
@ -317,10 +323,11 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion {
/**
* Set the "region of interest" by centering the given screen coordinate
* within the zoom region.
* @x The x-coord of the point to place at the center of the zoom region.
* @y The y-coord.
* @return Whether the shift was successful (for GS-mag, this is always
* true).
* @param {number} x: The x-coord of the point to place at the
* center of the zoom region.
* @param {number} y: The y-coord.
* @returns {bool} Whether the shift was successful (for GS-mag, this
* is always true).
*/
shiftContentsTo(x, y) {
this._zoomRegion.scrollContentsTo(x, y);
@ -330,8 +337,8 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion {
/**
* moveResize
* Sets the position and size of the ZoomRegion on screen.
* @viewPort Array, [left, top, right, bottom], defining the position and
* size on screen to place the zoom region.
* @param {number[]} viewPort: [left, top, right, bottom], defining
* the position and size on screen to place the zoom region.
*/
moveResize(viewPort) {
let viewRect = { x: viewPort[0], y: viewPort[1], width: viewPort[2] - viewPort[0], height: viewPort[3] - viewPort[1] };

View File

@ -189,7 +189,7 @@ function _initializeUI() {
messageTray = new MessageTray.MessageTray();
panel = new Panel.Panel();
keyboard = new Keyboard.Keyboard();
keyboard = new Keyboard.KeyboardManager();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
componentManager = new Components.ComponentManager();
@ -199,12 +199,12 @@ function _initializeUI() {
layoutManager.init();
overview.init();
(new PointerA11yTimeout.PointerA11yTimeout());
new PointerA11yTimeout.PointerA11yTimeout();
_a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
global.display.connect('overlay-key', () => {
if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
if (!_a11ySettings.get_boolean(STICKY_KEYS_ENABLE))
overview.toggle();
});
@ -248,17 +248,17 @@ function _initializeUI() {
}
layoutManager.connect('startup-complete', () => {
if (actionMode == Shell.ActionMode.NONE) {
if (actionMode == Shell.ActionMode.NONE)
actionMode = Shell.ActionMode.NORMAL;
}
if (screenShield) {
if (screenShield)
screenShield.lockIfWasLocked();
}
if (sessionMode.currentMode != 'gdm' &&
sessionMode.currentMode != 'initial-setup') {
GLib.log_structured(LOG_DOMAIN, GLib.LogLevelFlags.LEVEL_MESSAGE, {
'MESSAGE': `GNOME Shell started at ${_startDate}`,
'MESSAGE_ID': GNOMESHELL_STARTED_MESSAGE_ID
'MESSAGE_ID': GNOMESHELL_STARTED_MESSAGE_ID,
});
}
@ -289,19 +289,19 @@ function _initializeUI() {
function _getStylesheet(name) {
let stylesheet;
stylesheet = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/' + name);
stylesheet = Gio.File.new_for_uri(`resource:///org/gnome/shell/theme/${name}`);
if (stylesheet.query_exists(null))
return stylesheet;
let dataDirs = GLib.get_system_data_dirs();
for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'theme', name]);
let stylesheet = Gio.file_new_for_path(path);
stylesheet = Gio.file_new_for_path(path);
if (stylesheet.query_exists(null))
return stylesheet;
}
stylesheet = Gio.File.new_for_path(global.datadir + '/theme/' + name);
stylesheet = Gio.File.new_for_path(`${global.datadir}/theme/${name}`);
if (stylesheet.query_exists(null))
return stylesheet;
@ -337,7 +337,7 @@ function _loadDefaultStylesheet() {
*
* Get the theme CSS file that the shell will load
*
* Returns: A #GFile that contains the theme CSS,
* @returns {?Gio.File}: A #GFile that contains the theme CSS,
* null if using the default
*/
function getThemeStylesheet() {
@ -346,8 +346,8 @@ function getThemeStylesheet() {
/**
* setThemeStylesheet:
* @cssStylesheet: A file path that contains the theme CSS,
* set it to null to use the default
* @param {string=} cssStylesheet: A file path that contains the theme CSS,
* set it to null to use the default
*
* Set the theme CSS file that the shell will load
*/
@ -359,12 +359,12 @@ function reloadThemeResource() {
if (_themeResource)
_themeResource._unregister();
_themeResource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource');
_themeResource = Gio.Resource.load(`${global.datadir}/gnome-shell-theme.gresource`);
_themeResource._register();
}
function _loadOskLayouts() {
_oskResource = Gio.Resource.load(global.datadir + '/gnome-shell-osk-layouts.gresource');
_oskResource = Gio.Resource.load(`${global.datadir}/gnome-shell-osk-layouts.gresource`);
_oskResource._register();
}
@ -374,11 +374,13 @@ function _loadOskLayouts() {
* Reloads the theme CSS file
*/
function loadTheme() {
let themeContext = St.ThemeContext.get_for_stage (global.stage);
let themeContext = St.ThemeContext.get_for_stage(global.stage);
let previousTheme = themeContext.get_theme();
let theme = new St.Theme ({ application_stylesheet: _cssStylesheet,
default_stylesheet: _defaultCssStylesheet });
let theme = new St.Theme({
application_stylesheet: _cssStylesheet,
default_stylesheet: _defaultCssStylesheet,
});
if (theme.default_stylesheet == null)
throw new Error("No valid stylesheet found for '%s'".format(sessionMode.stylesheetName));
@ -390,26 +392,26 @@ function loadTheme() {
theme.load_stylesheet(customStylesheets[i]);
}
themeContext.set_theme (theme);
themeContext.set_theme(theme);
}
/**
* notify:
* @msg: A message
* @details: Additional information
* @param {string} msg: A message
* @param {string} details: Additional information
*/
function notify(msg, details) {
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.notify(notification);
source.showNotification(notification);
}
/**
* notifyError:
* @msg: An error message
* @details: Additional information
* @param {string} msg: An error message
* @param {string} details: Additional information
*
* See shell_global_notify_problem().
*/
@ -433,8 +435,8 @@ function _findModal(actor) {
/**
* pushModal:
* @actor: #ClutterActor which will be given keyboard focus
* @params: optional parameters
* @param {Clutter.Actor} actor: actor which will be given keyboard focus
* @param {Object=} params: optional parameters
*
* Ensure we are in a mode where all keyboard and mouse input goes to
* the stage, and focus @actor. Multiple calls to this function act in
@ -457,7 +459,7 @@ function _findModal(actor) {
* global keybindings; the default of NONE will filter
* out all keybindings
*
* Returns: true iff we successfully acquired a grab or already had one
* @returns {bool}: true iff we successfully acquired a grab or already had one
*/
function pushModal(actor, params) {
params = Params.parse(params, { timestamp: global.get_current_time(),
@ -488,11 +490,11 @@ function pushModal(actor, params) {
modalActorFocusStack[index].prevFocus = null;
});
}
modalActorFocusStack.push({ actor: actor,
modalActorFocusStack.push({ actor,
destroyId: actorDestroyId,
prevFocus: prevFocus,
prevFocusDestroyId: prevFocusDestroyId,
actionMode: actionMode });
prevFocus,
prevFocusDestroyId,
actionMode });
actionMode = params.actionMode;
global.stage.set_key_focus(actor);
@ -501,8 +503,9 @@ function pushModal(actor, params) {
/**
* popModal:
* @actor: #ClutterActor passed to original invocation of pushModal()
* @timestamp: optional timestamp
* @param {Clutter.Actor} actor: the actor passed to original invocation
* of pushModal()
* @param {number=} timestamp: optional timestamp
*
* Reverse the effect of pushModal(). If this invocation is undoing
* the topmost invocation, then the focus will be restored to the
@ -573,24 +576,24 @@ function popModal(actor, timestamp) {
}
function createLookingGlass() {
if (lookingGlass == null) {
if (lookingGlass == null)
lookingGlass = new LookingGlass.LookingGlass();
}
return lookingGlass;
}
function openRunDialog() {
if (runDialog == null) {
if (runDialog == null)
runDialog = new RunDialog.RunDialog();
}
runDialog.open();
}
/**
* activateWindow:
* @window: the Meta.Window to activate
* @time: (optional) current event time
* @workspaceNum: (optional) window's workspace number
* @param {Meta.Window} window: the window to activate
* @param {number=} time: current event time
* @param {number=} workspaceNum: window's workspace number
*
* Activates @window, switching to its workspace first if necessary,
* and switching out of the overview if it's currently active
@ -598,7 +601,7 @@ function openRunDialog() {
function activateWindow(window, time, workspaceNum) {
let workspaceManager = global.workspace_manager;
let activeWorkspaceNum = workspaceManager.get_active_workspace_index();
let windowWorkspaceNum = (workspaceNum !== undefined) ? workspaceNum : window.get_workspace().index();
let windowWorkspaceNum = workspaceNum !== undefined ? workspaceNum : window.get_workspace().index();
if (!time)
time = global.get_current_time();
@ -666,8 +669,8 @@ function _queueBeforeRedraw(workId) {
/**
* initializeDeferredWork:
* @actor: A #ClutterActor
* @callback: Function to invoke to perform work
* @param {Clutter.Actor} actor: an actor
* @param {callback} callback: Function to invoke to perform work
*
* This function sets up a callback to be invoked when either the
* given actor is mapped, or after some period of time when the machine
@ -680,13 +683,13 @@ function _queueBeforeRedraw(workId) {
* initialization as well, under the assumption that new actors
* will need it.
*
* Returns: A string work identifier
* @returns {string}: A string work identifier
*/
function initializeDeferredWork(actor, callback) {
// Turn into a string so we can use as an object property
let workId = `${(++_deferredWorkSequence)}`;
_deferredWorkData[workId] = { 'actor': actor,
'callback': callback };
let workId = `${++_deferredWorkSequence}`;
_deferredWorkData[workId] = { actor,
callback };
actor.connect('notify::mapped', () => {
if (!(actor.mapped && _deferredWorkQueue.includes(workId)))
return;
@ -704,7 +707,7 @@ function initializeDeferredWork(actor, callback) {
/**
* queueDeferredWork:
* @workId: work identifier
* @param {string} workId: work identifier
*
* Ensure that the work identified by @workId will be
* run on map or timeout. You should call this function
@ -740,12 +743,13 @@ class RestartMessage extends ModalDialog.ModalDialog {
shouldFadeIn: false,
destroyOnClose: true });
let label = new St.Label({ text: message });
let label = new St.Label({
text: message,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
});
this.contentLayout.add(label, { x_fill: false,
y_fill: false,
x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this.contentLayout.add_child(label);
this.buttonLayout.hide();
}
});

View File

@ -1,7 +1,8 @@
const { Atk, Clutter, Gio, GLib, GObject, Meta, Pango, St } = imports.gi;
/* exported MessageListSection */
const { Atk, Clutter, Gio, GLib,
GObject, Graphene, Meta, Pango, St } = imports.gi;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Signals = imports.signals;
const Calendar = imports.ui.calendar;
const Util = imports.misc.util;
@ -31,13 +32,18 @@ function _fixMarkup(text, allowMarkup) {
return GLib.markup_escape_text(text, -1);
}
var URLHighlighter = class URLHighlighter {
constructor(text = '', lineWrap, allowMarkup) {
this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter',
x_expand: true, x_align: Clutter.ActorAlign.START });
var URLHighlighter = GObject.registerClass(
class URLHighlighter extends St.Label {
_init(text = '', lineWrap, allowMarkup) {
super._init({
reactive: true,
style_class: 'url-highlighter',
x_expand: true,
x_align: Clutter.ActorAlign.START,
});
this._linkColor = '#ccccff';
this.actor.connect('style-changed', () => {
let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false);
this.connect('style-changed', () => {
let [hasColor, color] = this.get_theme_node().lookup_color('link-color', false);
if (hasColor) {
let linkColor = color.to_string().substr(0, 7);
if (linkColor != this._linkColor) {
@ -46,70 +52,75 @@ var URLHighlighter = class URLHighlighter {
}
}
});
this.actor.clutter_text.line_wrap = lineWrap;
this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
this.clutter_text.line_wrap = lineWrap;
this.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
this.setMarkup(text, allowMarkup);
this.actor.connect('button-press-event', (actor, event) => {
// Don't try to URL highlight when invisible.
// The MessageTray doesn't actually hide us, so
// we need to check for paint opacities as well.
if (!actor.visible || actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
}
// Keep Notification.actor from seeing this and taking
// a pointer grab, which would block our button-release-event
// handler, if an URL is clicked
return this._findUrlAtPos(event) != -1;
});
this.actor.connect('button-release-event', (actor, event) => {
if (!actor.visible || actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
let urlId = this._findUrlAtPos(event);
if (urlId != -1) {
let url = this._urls[urlId].url;
if (!url.includes(':'))
url = 'http://' + url;
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
return Clutter.EVENT_STOP;
}
vfunc_button_press_event(buttonEvent) {
// Don't try to URL highlight when invisible.
// The MessageTray doesn't actually hide us, so
// we need to check for paint opacities as well.
if (!this.visible || this.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
});
this.actor.connect('motion-event', (actor, event) => {
if (!actor.visible || actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
let urlId = this._findUrlAtPos(event);
if (urlId != -1 && !this._cursorChanged) {
global.display.set_cursor(Meta.Cursor.POINTING_HAND);
this._cursorChanged = true;
} else if (urlId == -1) {
global.display.set_cursor(Meta.Cursor.DEFAULT);
this._cursorChanged = false;
}
return Clutter.EVENT_PROPAGATE;
});
this.actor.connect('leave-event', () => {
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
// Keep Notification from seeing this and taking
// a pointer grab, which would block our button-release-event
// handler, if an URL is clicked
return this._findUrlAtPos(buttonEvent) != -1;
}
if (this._cursorChanged) {
this._cursorChanged = false;
global.display.set_cursor(Meta.Cursor.DEFAULT);
}
vfunc_button_release_event(buttonEvent) {
if (!this.visible || this.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
});
let urlId = this._findUrlAtPos(buttonEvent);
if (urlId != -1) {
let url = this._urls[urlId].url;
if (!url.includes(':'))
url = `http://${url}`;
Gio.app_info_launch_default_for_uri(
url, global.create_app_launch_context(0, -1));
return Clutter.EVENT_STOP;
}
return Clutter.EVENT_PROPAGATE;
}
vfunc_motion_event(motionEvent) {
if (!this.visible || this.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
let urlId = this._findUrlAtPos(motionEvent);
if (urlId != -1 && !this._cursorChanged) {
global.display.set_cursor(Meta.Cursor.POINTING_HAND);
this._cursorChanged = true;
} else if (urlId == -1) {
global.display.set_cursor(Meta.Cursor.DEFAULT);
this._cursorChanged = false;
}
return Clutter.EVENT_PROPAGATE;
}
vfunc_leave_event(crossingEvent) {
if (!this.visible || this.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
if (this._cursorChanged) {
this._cursorChanged = false;
global.display.set_cursor(Meta.Cursor.DEFAULT);
}
return super.vfunc_leave_event(crossingEvent);
}
setMarkup(text, allowMarkup) {
text = text ? _fixMarkup(text, allowMarkup) : '';
this._text = text;
this.actor.clutter_text.set_markup(text);
this.clutter_text.set_markup(text);
/* clutter_text.text contain text without markup */
this._urls = Util.findUrls(this.actor.clutter_text.text);
this._urls = Util.findUrls(this.clutter_text.text);
this._highlightUrls();
}
@ -121,33 +132,33 @@ var URLHighlighter = class URLHighlighter {
for (let i = 0; i < urls.length; i++) {
let url = urls[i];
let str = this._text.substr(pos, url.pos - pos);
markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>';
markup += `${str}<span foreground="${this._linkColor}"><u>${url.url}</u></span>`;
pos = url.pos + url.url.length;
}
markup += this._text.substr(pos);
this.actor.clutter_text.set_markup(markup);
this.clutter_text.set_markup(markup);
}
_findUrlAtPos(event) {
let success_;
let [x, y] = event.get_coords();
[success_, x, y] = this.actor.transform_stage_point(x, y);
let { x, y } = event;
[, x, y] = this.transform_stage_point(x, y);
let findPos = -1;
for (let i = 0; i < this.actor.clutter_text.text.length; i++) {
let [success_, px, py, lineHeight] = this.actor.clutter_text.position_to_coords(i);
for (let i = 0; i < this.clutter_text.text.length; i++) {
let [, px, py, lineHeight] = this.clutter_text.position_to_coords(i);
if (py > y || py + lineHeight < y || x < px)
continue;
findPos = i;
}
if (findPos != -1) {
for (let i = 0; i < this._urls.length; i++)
for (let i = 0; i < this._urls.length; i++) {
if (findPos >= this._urls[i].pos &&
this._urls[i].pos + this._urls[i].url.length > findPos)
return i;
}
}
return -1;
}
};
});
var ScaleLayout = GObject.registerClass(
class ScaleLayout extends Clutter.BinLayout {
@ -160,20 +171,22 @@ class ScaleLayout extends Clutter.BinLayout {
if (this._container == container)
return;
if (this._container)
if (this._container) {
for (let id of this._signals)
this._container.disconnect(id);
}
this._container = container;
this._signals = [];
if (this._container)
if (this._container) {
for (let signal of ['notify::scale-x', 'notify::scale-y']) {
let id = this._container.connect(signal, () => {
this.layout_changed();
});
this._signals.push(id);
}
}
}
vfunc_get_preferred_width(container, forHeight) {
@ -200,7 +213,7 @@ var LabelExpanderLayout = GObject.registerClass({
'Expansion of the layout, between 0 (collapsed) ' +
'and 1 (fully expanded',
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
0, 1, 0)
0, 1, 0),
},
}, class LabelExpanderLayout extends Clutter.LayoutManager {
_init(params) {
@ -222,7 +235,7 @@ var LabelExpanderLayout = GObject.registerClass({
let visibleIndex = this._expansion > 0 ? 1 : 0;
for (let i = 0; this._container && i < this._container.get_n_children(); i++)
this._container.get_child_at_index(i).visible = (i == visibleIndex);
this._container.get_child_at_index(i).visible = i == visibleIndex;
this.layout_changed();
}
@ -283,21 +296,31 @@ var LabelExpanderLayout = GObject.registerClass({
}
});
var Message = class Message {
constructor(title, body) {
this.expanded = false;
var Message = GObject.registerClass({
Signals: {
'close': {},
'expanded': {},
'unexpanded': {},
},
}, class Message extends St.Button {
_init(title, body) {
super._init({
style_class: 'message',
accessible_role: Atk.Role.NOTIFICATION,
can_focus: true,
x_expand: true,
y_expand: true,
});
this.expanded = false;
this._useBodyMarkup = false;
this.actor = new St.Button({ style_class: 'message',
accessible_role: Atk.Role.NOTIFICATION,
can_focus: true,
x_expand: true, x_fill: true });
this.actor.connect('key-press-event',
this._onKeyPressed.bind(this));
let vbox = new St.BoxLayout({ vertical: true });
this.actor.set_child(vbox);
let vbox = new St.BoxLayout({
vertical: true,
x_expand: true,
});
this.set_child(vbox);
let hbox = new St.BoxLayout();
vbox.add_actor(hbox);
@ -308,7 +331,7 @@ var Message = class Message {
this._iconBin = new St.Bin({ style_class: 'message-icon-bin',
y_expand: true,
y_align: St.Align.START,
y_align: Clutter.ActorAlign.START,
visible: false });
hbox.add_actor(this._iconBin);
@ -326,14 +349,18 @@ var Message = class Message {
this.setTitle(title);
titleBox.add_actor(this.titleLabel);
this._secondaryBin = new St.Bin({ style_class: 'message-secondary-bin',
x_expand: true, y_expand: true,
x_fill: true, y_fill: true });
this._secondaryBin = new St.Bin({
style_class: 'message-secondary-bin',
x_expand: true, y_expand: true,
});
titleBox.add_actor(this._secondaryBin);
let closeIcon = new St.Icon({ icon_name: 'window-close-symbolic',
icon_size: 16 });
this._closeButton = new St.Button({ child: closeIcon, opacity: 0 });
this._closeButton = new St.Button({
style_class: 'message-close-button',
child: closeIcon, opacity: 0,
});
titleBox.add_actor(this._closeButton);
this._bodyStack = new St.Widget({ x_expand: true });
@ -341,15 +368,14 @@ var Message = class Message {
contentBox.add_actor(this._bodyStack);
this.bodyLabel = new URLHighlighter('', false, this._useBodyMarkup);
this.bodyLabel.actor.add_style_class_name('message-body');
this._bodyStack.add_actor(this.bodyLabel.actor);
this.bodyLabel.add_style_class_name('message-body');
this._bodyStack.add_actor(this.bodyLabel);
this.setBody(body);
this._closeButton.connect('clicked', this.close.bind(this));
let actorHoverId = this.actor.connect('notify::hover', this._sync.bind(this));
this._closeButton.connect('destroy', this.actor.disconnect.bind(this.actor, actorHoverId));
this.actor.connect('clicked', this._onClicked.bind(this));
this.actor.connect('destroy', this._onDestroy.bind(this));
let actorHoverId = this.connect('notify::hover', this._sync.bind(this));
this._closeButton.connect('destroy', this.disconnect.bind(this, actorHoverId));
this.connect('destroy', this._onDestroy.bind(this));
this._sync();
}
@ -359,7 +385,7 @@ var Message = class Message {
setIcon(actor) {
this._iconBin.child = actor;
this._iconBin.visible = (actor != null);
this._iconBin.visible = actor != null;
}
setSecondaryActor(actor) {
@ -430,12 +456,12 @@ var Message = class Message {
expand(animate) {
this.expanded = true;
this._actionBin.visible = (this._actionBin.get_n_children() > 0);
this._actionBin.visible = this._actionBin.get_n_children() > 0;
if (this._bodyStack.get_n_children() < 2) {
this._expandedLabel = new URLHighlighter(this._bodyText,
true, this._useBodyMarkup);
this.setExpandedBody(this._expandedLabel.actor);
this.setExpandedBody(this._expandedLabel);
}
if (animate) {
@ -448,7 +474,7 @@ var Message = class Message {
this._actionBin.ease({
scale_y: 1,
duration: MessageTray.ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
} else {
this._bodyStack.layout_manager.expansion = 1;
@ -472,7 +498,7 @@ var Message = class Message {
onComplete: () => {
this._actionBin.hide();
this.expanded = false;
}
},
});
} else {
this._bodyStack.layout_manager.expansion = 0;
@ -488,19 +514,16 @@ var Message = class Message {
}
_sync() {
let visible = this.actor.hover && this.canClose();
let visible = this.hover && this.canClose();
this._closeButton.opacity = visible ? 255 : 0;
this._closeButton.reactive = visible;
}
_onClicked() {
}
_onDestroy() {
}
_onKeyPressed(a, event) {
let keysym = event.get_key_symbol();
vfunc_key_press_event(keyEvent) {
let keysym = keyEvent.keyval;
if (keysym == Clutter.KEY_Delete ||
keysym == Clutter.KEY_KP_Delete) {
@ -509,37 +532,66 @@ var Message = class Message {
}
return Clutter.EVENT_PROPAGATE;
}
};
Signals.addSignalMethods(Message.prototype);
});
var MessageListSection = class MessageListSection {
constructor() {
this.actor = new St.BoxLayout({ style_class: 'message-list-section',
clip_to_allocation: true,
x_expand: true, vertical: true });
var MessageListSection = GObject.registerClass({
Properties: {
'can-clear': GObject.ParamSpec.boolean(
'can-clear', 'can-clear', 'can-clear',
GObject.ParamFlags.READABLE,
false),
'empty': GObject.ParamSpec.boolean(
'empty', 'empty', 'empty',
GObject.ParamFlags.READABLE,
true),
},
Signals: {
'can-clear-changed': {},
'empty-changed': {},
'message-focused': { param_types: [Message.$gtype] },
},
}, class MessageListSection extends St.BoxLayout {
_init() {
super._init({
style_class: 'message-list-section',
clip_to_allocation: true,
vertical: true,
x_expand: true,
});
this._list = new St.BoxLayout({ style_class: 'message-list-section-list',
vertical: true });
this.actor.add_actor(this._list);
this.add_actor(this._list);
this._list.connect('actor-added', this._sync.bind(this));
this._list.connect('actor-removed', this._sync.bind(this));
let id = Main.sessionMode.connect('updated',
this._sync.bind(this));
this.actor.connect('destroy', () => {
this.connect('destroy', () => {
Main.sessionMode.disconnect(id);
});
this._messages = new Map();
this._date = new Date();
this.empty = true;
this.canClear = false;
this._empty = true;
this._canClear = false;
this._sync();
}
_onKeyFocusIn(actor) {
this.emit('key-focus-in', actor);
get empty() {
return this._empty;
}
get canClear() {
return this._canClear;
}
get _messages() {
return this._list.get_children().map(i => i.child);
}
_onKeyFocusIn(messageActor) {
this.emit('message-focused', messageActor);
}
get allowed() {
@ -558,94 +610,94 @@ var MessageListSection = class MessageListSection {
}
addMessageAtIndex(message, index, animate) {
let obj = {
container: null,
destroyId: 0,
keyFocusId: 0,
closeId: 0
};
let pivot = new Clutter.Point({ x: .5, y: .5 });
let scale = animate ? 0 : 1;
obj.container = new St.Widget({ layout_manager: new ScaleLayout(),
pivot_point: pivot,
scale_x: scale, scale_y: scale });
obj.keyFocusId = message.actor.connect('key-focus-in',
this._onKeyFocusIn.bind(this));
obj.destroyId = message.actor.connect('destroy', () => {
this.removeMessage(message, false);
if (this._messages.includes(message))
throw new Error('Message was already added previously');
let listItem = new St.Bin({
child: message,
layout_manager: new ScaleLayout(),
pivot_point: new Graphene.Point({ x: .5, y: .5 }),
});
obj.closeId = message.connect('close', () => {
listItem._connectionsIds = [];
listItem._connectionsIds.push(message.connect('key-focus-in',
this._onKeyFocusIn.bind(this)));
listItem._connectionsIds.push(message.connect('close', () => {
this.removeMessage(message, true);
});
}));
listItem._connectionsIds.push(message.connect('destroy', () => {
listItem._connectionsIds.forEach(id => message.disconnect(id));
listItem.destroy();
}));
this._messages.set(message, obj);
obj.container.add_actor(message.actor);
this._list.insert_child_at_index(listItem, index);
this._list.insert_child_at_index(obj.container, index);
if (animate)
obj.container.ease({
if (animate) {
listItem.set({ scale_x: 0, scale_y: 0 });
listItem.ease({
scale_x: 1,
scale_y: 1,
duration: MESSAGE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
}
moveMessage(message, index, animate) {
let obj = this._messages.get(message);
if (!this._messages.includes(message))
throw new Error(`Impossible to move untracked message`);
let listItem = message.get_parent();
if (!animate) {
this._list.set_child_at_index(obj.container, index);
this._list.set_child_at_index(listItem, index);
return;
}
let onComplete = () => {
this._list.set_child_at_index(obj.container, index);
obj.container.ease({
this._list.set_child_at_index(listItem, index);
listItem.ease({
scale_x: 1,
scale_y: 1,
duration: MESSAGE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
};
obj.container.ease({
listItem.ease({
scale_x: 0,
scale_y: 0,
duration: MESSAGE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete
onComplete,
});
}
removeMessage(message, animate) {
let obj = this._messages.get(message);
if (!this._messages.includes(message))
throw new Error(`Impossible to remove untracked message`);
message.actor.disconnect(obj.destroyId);
message.actor.disconnect(obj.keyFocusId);
message.disconnect(obj.closeId);
this._messages.delete(message);
let listItem = message.get_parent();
listItem._connectionsIds.forEach(id => message.disconnect(id));
if (animate) {
obj.container.ease({
listItem.ease({
scale_x: 0,
scale_y: 0,
duration: MESSAGE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
obj.container.destroy();
listItem.destroy();
global.sync_pointer();
}
},
});
} else {
obj.container.destroy();
listItem.destroy();
global.sync_pointer();
}
}
clear() {
let messages = [...this._messages.keys()].filter(msg => msg.canClose());
let messages = this._messages.filter(msg => msg.canClose());
// If there are few messages, letting them all zoom out looks OK
if (messages.length < 2) {
@ -658,46 +710,37 @@ var MessageListSection = class MessageListSection {
let delay = MESSAGE_ANIMATION_TIME / Math.max(messages.length, 5);
for (let i = 0; i < messages.length; i++) {
let message = messages[i];
let obj = this._messages.get(message);
obj.container.ease({
anchor_x: this._list.width,
message.get_parent().ease({
translation_x: this._list.width,
opacity: 0,
duration: MESSAGE_ANIMATION_TIME,
delay: i * delay,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => message.close()
onComplete: () => message.close(),
});
}
}
}
_canClear() {
for (let message of this._messages.keys())
if (message.canClose())
return true;
return false;
}
_shouldShow() {
return !this.empty;
}
_sync() {
let empty = this._list.get_n_children() == 0;
let changed = this.empty !== empty;
this.empty = empty;
let messages = this._messages;
let empty = messages.length == 0;
if (changed)
this.emit('empty-changed');
if (this._empty != empty) {
this._empty = empty;
this.notify('empty');
}
let canClear = this._canClear();
changed = this.canClear !== canClear;
this.canClear = canClear;
let canClear = messages.some(m => m.canClose());
if (this._canClear != canClear) {
this._canClear = canClear;
this.notify('can-clear');
}
if (changed)
this.emit('can-clear-changed');
this.actor.visible = this.allowed && this._shouldShow();
this.visible = this.allowed && this._shouldShow();
}
};
Signals.addSignalMethods(MessageListSection.prototype);
});

View File

@ -4,7 +4,6 @@
SystemNotificationSource, MessageTray */
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
const Signals = imports.signals;
const Calendar = imports.ui.calendar;
const GnomeSession = imports.misc.gnomeSession;
@ -34,7 +33,7 @@ var State = {
HIDDEN: 0,
SHOWING: 1,
SHOWN: 2,
HIDING: 3
HIDING: 3,
};
// These reasons are useful when we destroy the notifications received through
@ -48,7 +47,7 @@ var NotificationDestroyedReason = {
EXPIRED: 1,
DISMISSED: 2,
SOURCE_CLOSED: 3,
REPLACED: 4
REPLACED: 4,
};
// Message tray has its custom Urgency enumeration. LOW, NORMAL and CRITICAL
@ -59,7 +58,7 @@ var Urgency = {
LOW: 0,
NORMAL: 1,
HIGH: 2,
CRITICAL: 3
CRITICAL: 3,
};
// The privacy of the details of a notification. USER is for notifications which
@ -134,72 +133,82 @@ var FocusGrabber = class FocusGrabber {
// source, such as whether to play sound or honour the critical bit.
//
// A notification without a policy object will inherit the default one.
var NotificationPolicy = class NotificationPolicy {
constructor(params) {
params = Params.parse(params, {
enable: true,
enableSound: true,
showBanners: true,
forceExpanded: false,
showInLockScreen: true,
detailsInLockScreen: false,
});
Object.getOwnPropertyNames(params).forEach(key => {
let desc = Object.getOwnPropertyDescriptor(params, key);
Object.defineProperty(this, `_${key}`, desc);
});
}
var NotificationPolicy = GObject.registerClass({
Properties: {
'enable': GObject.ParamSpec.boolean(
'enable', 'enable', 'enable',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
true),
'enable-sound': GObject.ParamSpec.boolean(
'enable-sound', 'enable-sound', 'enable-sound',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
true),
'show-banners': GObject.ParamSpec.boolean(
'show-banners', 'show-banners', 'show-banners',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
true),
'force-expanded': GObject.ParamSpec.boolean(
'force-expanded', 'force-expanded', 'force-expanded',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
false),
'show-in-lock-screen': GObject.ParamSpec.boolean(
'show-in-lock-screen', 'show-in-lock-screen', 'show-in-lock-screen',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
false),
'details-in-lock-screen': GObject.ParamSpec.boolean(
'details-in-lock-screen', 'details-in-lock-screen', 'details-in-lock-screen',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
false),
},
}, class NotificationPolicy extends GObject.Object {
// Do nothing for the default policy. These methods are only useful for the
// GSettings policy.
store() { }
destroy() { }
get enable() {
return this._enable;
destroy() {
this.run_dispose();
}
get enableSound() {
return this._enableSound;
return this.enable_sound;
}
get showBanners() {
return this._showBanners;
return this.show_banners;
}
get forceExpanded() {
return this._forceExpanded;
return this.force_expanded;
}
get showInLockScreen() {
return this._showInLockScreen;
return this.show_in_lock_screen;
}
get detailsInLockScreen() {
return this._detailsInLockScreen;
return this.details_in_lock_screen;
}
};
Signals.addSignalMethods(NotificationPolicy.prototype);
});
var NotificationGenericPolicy =
class NotificationGenericPolicy extends NotificationPolicy {
constructor() {
super();
var NotificationGenericPolicy = GObject.registerClass({
}, class NotificationGenericPolicy extends NotificationPolicy {
_init() {
super._init();
this.id = 'generic';
this._masterSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications' });
this._masterSettings.connect('changed', this._changed.bind(this));
}
store() { }
destroy() {
this._masterSettings.run_dispose();
super.destroy();
}
_changed(settings, key) {
this.emit('policy-changed', key);
if (this.constructor.find_property(key))
this.notify(key);
}
get showBanners() {
@ -209,12 +218,12 @@ class NotificationGenericPolicy extends NotificationPolicy {
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen');
}
};
});
var NotificationApplicationPolicy =
class NotificationApplicationPolicy extends NotificationPolicy {
constructor(id) {
super();
var NotificationApplicationPolicy = GObject.registerClass({
}, class NotificationApplicationPolicy extends NotificationPolicy {
_init(id) {
super._init();
this.id = id;
this._canonicalId = this._canonicalizeId(id);
@ -240,12 +249,13 @@ class NotificationApplicationPolicy extends NotificationPolicy {
destroy() {
this._masterSettings.run_dispose();
this._settings.run_dispose();
super.destroy();
}
_changed(settings, key) {
this.emit('policy-changed', key);
if (key == 'enable')
this.emit('enable-changed');
if (this.constructor.find_property(key))
this.notify(key);
}
_canonicalizeId(id) {
@ -279,7 +289,7 @@ class NotificationApplicationPolicy extends NotificationPolicy {
get detailsInLockScreen() {
return this._settings.get_boolean('details-in-lock-screen');
}
};
});
// Notification:
// @source: the notification's Source
@ -336,12 +346,25 @@ class NotificationApplicationPolicy extends NotificationPolicy {
// @source allows playing sounds).
//
// [1] https://developer.gnome.org/notification-spec/#markup
var Notification = class Notification {
constructor(source, title, banner, params) {
var Notification = GObject.registerClass({
Properties: {
'acknowledged': GObject.ParamSpec.boolean(
'acknowledged', 'acknowledged', 'acknowledged',
GObject.ParamFlags.READWRITE,
false),
},
Signals: {
'activated': {},
'destroy': { param_types: [GObject.TYPE_UINT] },
'updated': { param_types: [GObject.TYPE_BOOLEAN] },
},
}, class Notification extends GObject.Object {
_init(source, title, banner, params) {
super._init();
this.source = source;
this.title = title;
this.urgency = Urgency.NORMAL;
this.resident = false;
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
this.isTransient = false;
this.privacyScope = PrivacyScope.USER;
@ -353,6 +376,7 @@ var Notification = class Notification {
this._soundFile = null;
this._soundPlayed = false;
this.actions = [];
this.setResident(false);
// If called with only one argument we assume the caller
// will call .update() later on. This is the case of
@ -411,7 +435,7 @@ var Notification = class Notification {
// @label: the label for the action's button
// @callback: the callback for the action
addAction(label, callback) {
this.actions.push({ label: label, callback: callback });
this.actions.push({ label, callback });
}
get acknowledged() {
@ -422,7 +446,7 @@ var Notification = class Notification {
if (this._acknowledged == v)
return;
this._acknowledged = v;
this.emit('acknowledged-changed');
this.notify('acknowledged');
}
setUrgency(urgency) {
@ -431,6 +455,15 @@ var Notification = class Notification {
setResident(resident) {
this.resident = resident;
if (this.resident) {
if (this._activatedId) {
this.disconnect(this._activatedId);
this._activatedId = 0;
}
} else if (!this._activatedId) {
this._activatedId = this.connect_after('activated', () => this.destroy());
}
}
setTransient(isTransient) {
@ -472,23 +505,30 @@ var Notification = class Notification {
activate() {
this.emit('activated');
if (!this.resident)
this.destroy();
}
destroy(reason = NotificationDestroyedReason.DISMISSED) {
if (this._activatedId) {
this.disconnect(this._activatedId);
delete this._activatedId;
}
this.emit('destroy', reason);
this.run_dispose();
}
};
Signals.addSignalMethods(Notification.prototype);
});
var NotificationBanner =
class NotificationBanner extends Calendar.NotificationMessage {
constructor(notification) {
super(notification);
var NotificationBanner = GObject.registerClass({
Signals: {
'done-displaying': {},
'unfocused': {},
},
}, class NotificationBanner extends Calendar.NotificationMessage {
_init(notification) {
super._init(notification);
this.actor.can_focus = false;
this.actor.add_style_class_name('notification-banner');
this.can_focus = false;
this.add_style_class_name('notification-banner');
this._buttonBox = null;
@ -569,13 +609,13 @@ class NotificationBanner extends Calendar.NotificationMessage {
addAction(label, callback) {
let button = new St.Button({ style_class: 'notification-button',
label: label,
label,
x_expand: true,
can_focus: true });
return this.addButton(button, callback);
}
};
});
var SourceActor = GObject.registerClass(
class SourceActor extends St.Widget {
@ -592,8 +632,7 @@ class SourceActor extends St.Widget {
this._actorDestroyed = false;
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._iconBin = new St.Bin({ x_fill: true,
x_expand: true,
this._iconBin = new St.Bin({ x_expand: true,
height: size * scaleFactor,
width: size * scaleFactor });
@ -640,7 +679,7 @@ class SourceActorWithLabel extends SourceActor {
this.add_actor(this._counterBin);
this._countUpdatedId = this._source.connect('count-updated', this._updateCount.bind(this));
this._countUpdatedId = this._source.connect('notify::count', this._updateCount.bind(this));
this._updateCount();
this.connect('destroy', () => {
@ -688,11 +727,33 @@ class SourceActorWithLabel extends SourceActor {
}
});
var Source = class Source {
constructor(title, iconName) {
var Source = GObject.registerClass({
Properties: {
'count': GObject.ParamSpec.int(
'count', 'count', 'count',
GObject.ParamFlags.READABLE,
0, GLib.MAXINT32, 0),
'policy': GObject.ParamSpec.object(
'policy', 'policy', 'policy',
GObject.ParamFlags.READWRITE,
NotificationPolicy.$gtype),
'title': GObject.ParamSpec.string(
'title', 'title', 'title',
GObject.ParamFlags.READWRITE,
null),
},
Signals: {
'destroy': { param_types: [GObject.TYPE_UINT] },
'icon-updated': {},
'notification-added': { param_types: [Notification.$gtype] },
'notification-show': { param_types: [Notification.$gtype] },
},
}, class Source extends GObject.Object {
_init(title, iconName) {
super._init({ title });
this.SOURCE_ICON_SIZE = 48;
this.title = title;
this.iconName = iconName;
this.isChat = false;
@ -727,7 +788,7 @@ var Source = class Source {
}
countUpdated() {
this.emit('count-updated');
super.notify('count');
}
_createPolicy() {
@ -741,8 +802,11 @@ var Source = class Source {
}
setTitle(newTitle) {
if (this.title == newTitle)
return;
this.title = newTitle;
this.emit('title-changed');
this.notify('title');
}
createBanner(notification) {
@ -767,10 +831,10 @@ var Source = class Source {
return;
this.notifications.splice(index, 1);
this.countUpdated();
if (this.notifications.length == 0)
this.destroy();
this.countUpdated();
}
pushNotification(notification) {
@ -781,22 +845,36 @@ var Source = class Source {
this.notifications.shift().destroy(NotificationDestroyedReason.EXPIRED);
notification.connect('destroy', this._onNotificationDestroy.bind(this));
notification.connect('acknowledged-changed', this.countUpdated.bind(this));
notification.connect('notify::acknowledged', this.countUpdated.bind(this));
this.notifications.push(notification);
this.emit('notification-added', notification);
this.countUpdated();
}
notify(notification) {
showNotification(notification) {
notification.acknowledged = false;
this.pushNotification(notification);
if (this.policy.showBanners || notification.urgency == Urgency.CRITICAL) {
this.emit('notify', notification);
} else {
if (this.policy.showBanners || notification.urgency == Urgency.CRITICAL)
this.emit('notification-show', notification);
else
notification.playSound();
}
notify(propName) {
if (propName instanceof Notification) {
try {
throw new Error('Source.notify() has been moved to Source.showNotification()' +
'this code will break in the future');
} catch (e) {
logError(e);
this.showNotification(propName);
return;
}
}
super.notify(propName);
}
destroy(reason) {
@ -809,6 +887,8 @@ var Source = class Source {
notifications[i].destroy(reason);
this.emit('destroy', reason);
this.run_dispose();
}
iconUpdated() {
@ -820,17 +900,27 @@ var Source = class Source {
}
destroyNonResidentNotifications() {
for (let i = this.notifications.length - 1; i >= 0; i--)
for (let i = this.notifications.length - 1; i >= 0; i--) {
if (!this.notifications[i].resident)
this.notifications[i].destroy();
this.countUpdated();
}
}
};
Signals.addSignalMethods(Source.prototype);
});
var MessageTray = GObject.registerClass({
Signals: {
'queue-changed': {},
'source-added': { param_types: [Source.$gtype] },
'source-removed': { param_types: [Source.$gtype] },
},
}, class MessageTray extends St.Widget {
_init() {
super._init({
visible: false,
clip_to_allocation: true,
layout_manager: new Clutter.BinLayout(),
});
var MessageTray = class MessageTray {
constructor() {
this._presence = new GnomeSession.Presence((proxy, _error) => {
this._onStatusChanged(proxy.status);
});
@ -847,18 +937,15 @@ var MessageTray = class MessageTray {
// so fix up Clutter's view of the pointer position in
// that case.
let related = ev.get_related();
if (!related || this.actor.contains(related))
if (!related || this.contains(related))
global.sync_pointer();
});
this.actor = new St.Widget({ visible: false,
clip_to_allocation: true,
layout_manager: new Clutter.BinLayout() });
let constraint = new Layout.MonitorConstraint({ primary: true });
Main.layoutManager.panelBox.bind_property('visible',
constraint, 'work-area',
GObject.BindingFlags.SYNC_CREATE);
this.actor.add_constraint(constraint);
this.add_constraint(constraint);
this._bannerBin = new St.Widget({ name: 'notification-container',
reactive: true,
@ -872,7 +959,7 @@ var MessageTray = class MessageTray {
this._onNotificationKeyRelease.bind(this));
this._bannerBin.connect('notify::hover',
this._onNotificationHoverChanged.bind(this));
this.actor.add_actor(this._bannerBin);
this.add_actor(this._bannerBin);
this._notificationFocusGrabber = new FocusGrabber(this._bannerBin);
this._notificationQueue = [];
@ -901,7 +988,7 @@ var MessageTray = class MessageTray {
this._notificationTimeoutId = 0;
this._notificationRemoved = false;
Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
Main.layoutManager.addChrome(this, { affectsInputRegion: false });
Main.layoutManager.trackChrome(this._bannerBin, { affectsInputRegion: true });
global.display.connect('in-fullscreen-changed', this._updateState.bind(this));
@ -944,11 +1031,11 @@ var MessageTray = class MessageTray {
}
_onDragBegin() {
Shell.util_set_hidden_from_pick(this.actor, true);
Shell.util_set_hidden_from_pick(this, true);
}
_onDragEnd() {
Shell.util_set_hidden_from_pick(this.actor, false);
Shell.util_set_hidden_from_pick(this, false);
}
get bannerAlignment() {
@ -997,22 +1084,22 @@ var MessageTray = class MessageTray {
// Register that we got a notification for this source
source.policy.store();
source.policy.connect('enable-changed', () => {
source.policy.connect('notify::enable', () => {
this._onSourceEnableChanged(source.policy, source);
});
source.policy.connect('policy-changed', this._updateState.bind(this));
source.policy.connect('notify', this._updateState.bind(this));
this._onSourceEnableChanged(source.policy, source);
}
_addSource(source) {
let obj = {
notifyId: 0,
showId: 0,
destroyId: 0,
};
this._sources.set(source, obj);
obj.notifyId = source.connect('notify', this._onNotify.bind(this));
obj.showId = source.connect('notification-show', this._onNotificationShow.bind(this));
obj.destroyId = source.connect('destroy', this._onSourceDestroy.bind(this));
this.emit('source-added', source);
@ -1022,7 +1109,7 @@ var MessageTray = class MessageTray {
let obj = this._sources.get(source);
this._sources.delete(source);
source.disconnect(obj.notifyId);
source.disconnect(obj.showId);
source.disconnect(obj.destroyId);
this.emit('source-removed', source);
@ -1063,7 +1150,7 @@ var MessageTray = class MessageTray {
}
}
_onNotify(source, notification) {
_onNotificationShow(_source, notification) {
if (this._notification == notification) {
// If a notification that is being shown is updated, we update
// how it is shown and extend the time until it auto-hides.
@ -1075,7 +1162,7 @@ var MessageTray = class MessageTray {
// indicator in the panel; however do make an exception for CRITICAL
// notifications, as only banner mode allows expansion.
let bannerCount = this._notification ? 1 : 0;
let full = (this.queueCount + bannerCount >= MAX_NOTIFICATIONS_IN_QUEUE);
let full = this.queueCount + bannerCount >= MAX_NOTIFICATIONS_IN_QUEUE;
if (!full || notification.urgency == Urgency.CRITICAL) {
notification.connect('destroy',
this._onNotificationDestroy.bind(this));
@ -1195,7 +1282,7 @@ var MessageTray = class MessageTray {
// at the present time.
_updateState() {
let hasMonitor = Main.layoutManager.primaryMonitor != null;
this.actor.visible = !this._bannerBlocked && hasMonitor && this._banner != null;
this.visible = !this._bannerBlocked && hasMonitor && this._banner != null;
if (this._bannerBlocked || !hasMonitor)
return;
@ -1222,7 +1309,7 @@ var MessageTray = class MessageTray {
let nextNotification = this._notificationQueue[0] || null;
if (hasNotifications && nextNotification) {
let limited = this._busy || Main.layoutManager.primaryMonitor.inFullscreen;
let showNextNotification = (!limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL);
let showNextNotification = !limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL;
if (showNextNotification)
this._showNotification();
}
@ -1232,7 +1319,7 @@ var MessageTray = class MessageTray {
this._notification.urgency != Urgency.CRITICAL &&
!this._banner.focused &&
!this._pointerInNotification) || this._notificationExpired;
let mustClose = (this._notificationRemoved || !hasNotifications || expired);
let mustClose = this._notificationRemoved || !hasNotifications || expired;
if (mustClose) {
let animate = hasNotifications && !this._notificationRemoved;
@ -1275,11 +1362,11 @@ var MessageTray = class MessageTray {
this._updateState();
});
this._bannerBin.add_actor(this._banner.actor);
this._bannerBin.add_actor(this._banner);
this._bannerBin.opacity = 0;
this._bannerBin.y = -this._banner.actor.height;
this.actor.show();
this._bannerBin.y = -this._banner.height;
this.show();
Meta.disable_unredirect_for_display(global.display);
this._updateShowingNotification();
@ -1327,7 +1414,7 @@ var MessageTray = class MessageTray {
this._bannerBin.ease({
opacity: 255,
duration: ANIMATION_TIME,
mode: Clutter.AnimationMode.LINEAR
mode: Clutter.AnimationMode.LINEAR,
});
this._bannerBin.ease({
y: 0,
@ -1337,7 +1424,7 @@ var MessageTray = class MessageTray {
this._notificationState = State.SHOWN;
this._showNotificationCompleted();
this._updateState();
}
},
});
}
@ -1403,7 +1490,7 @@ var MessageTray = class MessageTray {
this._bannerBin.ease({
opacity: 0,
duration: ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_BACK
mode: Clutter.AnimationMode.EASE_OUT_BACK,
});
this._bannerBin.ease({
y: -this._bannerBin.height,
@ -1413,7 +1500,7 @@ var MessageTray = class MessageTray {
this._notificationState = State.HIDDEN;
this._hideNotificationCompleted();
this._updateState();
}
},
});
} else {
this._bannerBin.y = -this._bannerBin.height;
@ -1426,16 +1513,16 @@ var MessageTray = class MessageTray {
_hideNotificationCompleted() {
let notification = this._notification;
this._notification = null;
if (notification.isTransient)
if (!this._notificationRemoved && notification.isTransient)
notification.destroy(NotificationDestroyedReason.EXPIRED);
this._pointerInNotification = false;
this._notificationRemoved = false;
Meta.enable_unredirect_for_display(global.display);
this._banner.actor.destroy();
this._banner.destroy();
this._banner = null;
this.actor.hide();
this.hide();
}
_expandActiveNotification() {
@ -1457,15 +1544,15 @@ var MessageTray = class MessageTray {
_ensureBannerFocused() {
this._notificationFocusGrabber.grabFocus();
}
};
Signals.addSignalMethods(MessageTray.prototype);
});
var SystemNotificationSource = class SystemNotificationSource extends Source {
constructor() {
super(_("System Information"), 'dialog-information-symbolic');
var SystemNotificationSource = GObject.registerClass(
class SystemNotificationSource extends Source {
_init() {
super._init(_("System Information"), 'dialog-information-symbolic');
}
open() {
this.destroy();
}
};
});

View File

@ -17,7 +17,7 @@ var State = {
CLOSED: 1,
OPENING: 2,
CLOSING: 3,
FADED_OUT: 4
FADED_OUT: 4,
};
var ModalDialog = GObject.registerClass({
@ -26,9 +26,9 @@ var ModalDialog = GObject.registerClass({
GObject.ParamFlags.READABLE,
Math.min(...Object.values(State)),
Math.max(...Object.values(State)),
State.CLOSED)
State.CLOSED),
},
Signals: { 'opened': {}, 'closed': {} }
Signals: { 'opened': {}, 'closed': {} },
}, class ModalDialog extends St.Widget {
_init(params) {
super._init({ visible: false,
@ -57,9 +57,12 @@ var ModalDialog = GObject.registerClass({
coordinate: Clutter.BindCoordinate.ALL });
this.add_constraint(constraint);
this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._backgroundBin = new St.Bin({ child: this.backgroundStack,
x_fill: true, y_fill: true });
this.backgroundStack = new St.Widget({
layout_manager: new Clutter.BinLayout(),
x_expand: true,
y_expand: true,
});
this._backgroundBin = new St.Bin({ child: this.backgroundStack });
this._monitorConstraint = new Layout.MonitorConstraint();
this._backgroundBin.add_constraint(this._monitorConstraint);
this.add_actor(this._backgroundBin);
@ -121,7 +124,7 @@ var ModalDialog = GObject.registerClass({
this.dialogLayout.opacity = 255;
if (this._lightbox)
this._lightbox.show();
this._lightbox.lightOn();
this.opacity = 0;
this.show();
this.ease({
@ -131,7 +134,7 @@ var ModalDialog = GObject.registerClass({
onComplete: () => {
this._setState(State.OPENED);
this.emit('opened');
}
},
});
}
@ -180,7 +183,7 @@ var ModalDialog = GObject.registerClass({
opacity: 0,
duration: OPEN_AND_CLOSE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._closeComplete()
onComplete: () => this._closeComplete(),
});
} else {
this._closeComplete();
@ -203,7 +206,7 @@ var ModalDialog = GObject.registerClass({
this._hasModal = false;
if (!this._shellReactive)
this._eventBlocker.raise_top();
this.backgroundStack.set_child_above_sibling(this._eventBlocker, null);
}
pushModal(timestamp) {
@ -216,6 +219,8 @@ var ModalDialog = GObject.registerClass({
if (!Main.pushModal(this, params))
return false;
Main.layoutManager.emit('system-modal-opened');
this._hasModal = true;
if (this._savedKeyFocus) {
this._savedKeyFocus.grab_key_focus();
@ -226,7 +231,7 @@ var ModalDialog = GObject.registerClass({
}
if (!this._shellReactive)
this._eventBlocker.lower_bottom();
this.backgroundStack.set_child_below_sibling(this._eventBlocker, null);
return true;
}
@ -253,7 +258,7 @@ var ModalDialog = GObject.registerClass({
opacity: 0,
duration: FADE_OUT_DIALOG_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => (this.state = State.FADED_OUT)
onComplete: () => (this.state = State.FADED_OUT),
});
}
});

View File

@ -1,5 +1,5 @@
/* exported MediaSection */
const { Gio, Shell, St } = imports.gi;
const { Gio, GObject, Shell, St } = imports.gi;
const Signals = imports.signals;
const Calendar = imports.ui.calendar;
@ -19,9 +19,10 @@ const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface);
const MPRIS_PLAYER_PREFIX = 'org.mpris.MediaPlayer2.';
var MediaMessage = class MediaMessage extends MessageList.Message {
constructor(player) {
super('', '');
var MediaMessage = GObject.registerClass(
class MediaMessage extends MessageList.Message {
_init(player) {
super._init('', '');
this._player = player;
@ -43,12 +44,20 @@ var MediaMessage = class MediaMessage extends MessageList.Message {
this._player.next();
});
this._player.connect('changed', this._update.bind(this));
this._player.connect('closed', this.close.bind(this));
this._updateHandlerId =
this._player.connect('changed', this._update.bind(this));
this._closedHandlerId =
this._player.connect('closed', this.close.bind(this));
this._update();
}
_onClicked() {
_onDestroy() {
super._onDestroy();
this._player.disconnect(this._updateHandlerId);
this._player.disconnect(this._closedHandlerId);
}
vfunc_clicked() {
this._player.raise();
Main.panel.closeCalendar();
}
@ -63,7 +72,7 @@ var MediaMessage = class MediaMessage extends MessageList.Message {
if (this._player.trackCoverUrl) {
let file = Gio.File.new_for_uri(this._player.trackCoverUrl);
this._icon.gicon = new Gio.FileIcon({ file: file });
this._icon.gicon = new Gio.FileIcon({ file });
this._icon.remove_style_class_name('fallback');
} else {
this._icon.icon_name = 'audio-x-generic-symbolic';
@ -79,7 +88,7 @@ var MediaMessage = class MediaMessage extends MessageList.Message {
this._updateNavButton(this._prevButton, this._player.canGoPrevious);
this._updateNavButton(this._nextButton, this._player.canGoNext);
}
};
});
var MprisPlayer = class MprisPlayer {
constructor(busName) {
@ -94,6 +103,7 @@ var MprisPlayer = class MprisPlayer {
this._trackArtists = [];
this._trackTitle = '';
this._trackCoverUrl = '';
this._busName = busName;
}
get status() {
@ -176,9 +186,39 @@ var MprisPlayer = class MprisPlayer {
for (let prop in this._playerProxy.Metadata)
metadata[prop] = this._playerProxy.Metadata[prop].deep_unpack();
this._trackArtists = metadata['xesam:artist'] || [_("Unknown artist")];
this._trackTitle = metadata['xesam:title'] || _("Unknown title");
this._trackCoverUrl = metadata['mpris:artUrl'] || '';
// Validate according to the spec; some clients send buggy metadata:
// https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata
this._trackArtists = metadata['xesam:artist'];
if (!Array.isArray(this._trackArtists) ||
!this._trackArtists.every(artist => typeof artist === 'string')) {
if (typeof this._trackArtists !== 'undefined') {
log(`Received faulty track artist metadata from ${
this._busName}; expected an array of strings, got ${
this._trackArtists} (${typeof this._trackArtists})`);
}
this._trackArtists = [_("Unknown artist")];
}
this._trackTitle = metadata['xesam:title'];
if (typeof this._trackTitle !== 'string') {
if (typeof this._trackTitle !== 'undefined') {
log(`Received faulty track title metadata from ${
this._busName}; expected a string, got ${
this._trackTitle} (${typeof this._trackTitle})`);
}
this._trackTitle = _("Unknown title");
}
this._trackCoverUrl = metadata['mpris:artUrl'];
if (typeof this._trackCoverUrl !== 'string') {
if (typeof this._trackCoverUrl !== 'undefined') {
log(`Received faulty track cover art metadata from ${
this._busName}; expected a string, got ${
this._trackCoverUrl} (${typeof this._trackCoverUrl})`);
}
this._trackCoverUrl = '';
}
this.emit('changed');
let visible = this._playerProxy.CanPlay;
@ -188,15 +228,16 @@ var MprisPlayer = class MprisPlayer {
if (visible)
this.emit('show');
else
this._close();
this.emit('hide');
}
}
};
Signals.addSignalMethods(MprisPlayer.prototype);
var MediaSection = class MediaSection extends MessageList.MessageListSection {
constructor() {
super();
var MediaSection = GObject.registerClass(
class MediaSection extends MessageList.MessageListSection {
_init() {
super._init();
this._players = new Map();
@ -215,15 +256,20 @@ var MediaSection = class MediaSection extends MessageList.MessageListSection {
return;
let player = new MprisPlayer(busName);
let message = null;
player.connect('closed',
() => {
this._players.delete(busName);
});
player.connect('show',
() => {
let message = new MediaMessage(player);
this.addMessage(message, true);
});
player.connect('show', () => {
message = new MediaMessage(player);
this.addMessage(message, true);
});
player.connect('hide', () => {
this.removeMessage(message, true);
message = null;
});
this._players.set(busName, player);
}
@ -247,4 +293,4 @@ var MediaSection = class MediaSection extends MessageList.MessageListSection {
if (newOwner && !oldOwner)
this._addPlayer(name);
}
};
});

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported NotificationDaemon */
const { GdkPixbuf, Gio, GLib, Shell, St } = imports.gi;
const { GdkPixbuf, Gio, GLib, GObject, Shell, St } = imports.gi;
const Config = imports.misc.config;
const Main = imports.ui.main;
@ -23,13 +23,13 @@ var NotificationClosedReason = {
EXPIRED: 1,
DISMISSED: 2,
APP_CLOSED: 3,
UNDEFINED: 4
UNDEFINED: 4,
};
var Urgency = {
LOW: 0,
NORMAL: 1,
CRITICAL: 2
CRITICAL: 2,
};
const rewriteRules = {
@ -39,8 +39,8 @@ const rewriteRules = {
{ pattern: /^XChat: New public message from: (\S*) \((.*)\)$/,
replacement: '$2 <$1>' },
{ pattern: /^XChat: Highlighted message from: (\S*) \((.*)\)$/,
replacement: '$2 <$1>' }
]
replacement: '$2 <$1>' },
],
};
var FdoNotificationDaemon = class FdoNotificationDaemon {
@ -201,13 +201,13 @@ var FdoNotificationDaemon = class FdoNotificationDaemon {
hints['image-data'] = hints['icon_data'];
}
let ndata = { appName: appName,
icon: icon,
summary: summary,
body: body,
actions: actions,
hints: hints,
timeout: timeout };
let ndata = { appName,
icon,
summary,
body,
actions,
hints,
timeout };
if (replacesId != 0 && this._notifications[replacesId]) {
ndata.id = id = replacesId;
ndata.notification = this._notifications[replacesId].notification;
@ -245,7 +245,7 @@ var FdoNotificationDaemon = class FdoNotificationDaemon {
return;
}
let [pid] = result;
[pid] = result;
source = this._getSource(appName, pid, ndata, sender, null);
this._senderToPid[sender] = pid;
@ -299,7 +299,7 @@ var FdoNotificationDaemon = class FdoNotificationDaemon {
else if (!gicon)
gicon = this._fallbackIconForNotificationData(hints);
notification.update(summary, body, { gicon: gicon,
notification.update(summary, body, { gicon,
bannerMarkup: true,
clear: true,
soundFile: hints['sound-file'],
@ -310,12 +310,13 @@ var FdoNotificationDaemon = class FdoNotificationDaemon {
if (actions.length) {
for (let i = 0; i < actions.length - 1; i += 2) {
let [actionId, label] = [actions[i], actions[i + 1]];
if (actionId == 'default')
if (actionId == 'default') {
hasDefaultAction = true;
else
} else {
notification.addAction(label, () => {
this._emitActionInvoked(ndata.id, actionId);
});
}
}
}
@ -345,7 +346,7 @@ var FdoNotificationDaemon = class FdoNotificationDaemon {
// of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(!!hints['transient']);
let privacyScope = (hints['x-gnome-privacy-scope'] || 'user');
let privacyScope = hints['x-gnome-privacy-scope'] || 'user';
notification.setPrivacyScope(privacyScope == 'system'
? MessageTray.PrivacyScope.SYSTEM
: MessageTray.PrivacyScope.USER);
@ -383,7 +384,7 @@ var FdoNotificationDaemon = class FdoNotificationDaemon {
Config.PACKAGE_NAME,
'GNOME',
Config.PACKAGE_VERSION,
'1.2'
'1.2',
];
}
@ -412,10 +413,10 @@ var FdoNotificationDaemon = class FdoNotificationDaemon {
}
};
var FdoNotificationDaemonSource =
var FdoNotificationDaemonSource = GObject.registerClass(
class FdoNotificationDaemonSource extends MessageTray.Source {
constructor(title, pid, sender, appId) {
super(title);
_init(title, pid, sender, appId) {
super._init(title);
this.pid = pid;
this.app = this._getApp(appId);
@ -427,13 +428,14 @@ class FdoNotificationDaemonSource extends MessageTray.Source {
else
this.useNotificationIcon = true;
if (sender)
if (sender) {
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
Gio.BusNameWatcherFlags.NONE,
null,
this._onNameVanished.bind(this));
else
} else {
this._nameWatcherId = 0;
}
}
_createPolicy() {
@ -464,7 +466,7 @@ class FdoNotificationDaemonSource extends MessageTray.Source {
if (notification.resident && this.app && tracker.focus_app == this.app)
this.pushNotification(notification);
else
this.notify(notification);
this.showNotification(notification);
}
_getApp(appId) {
@ -526,19 +528,19 @@ class FdoNotificationDaemonSource extends MessageTray.Source {
return null;
}
}
};
});
const PRIORITY_URGENCY_MAP = {
low: MessageTray.Urgency.LOW,
normal: MessageTray.Urgency.NORMAL,
high: MessageTray.Urgency.HIGH,
urgent: MessageTray.Urgency.CRITICAL
urgent: MessageTray.Urgency.CRITICAL,
};
var GtkNotificationDaemonNotification =
var GtkNotificationDaemonNotification = GObject.registerClass(
class GtkNotificationDaemonNotification extends MessageTray.Notification {
constructor(source, notification) {
super(source);
_init(source, notification) {
super._init(source);
this._serialized = GLib.Variant.new('a{sv}', notification);
let { title,
@ -602,7 +604,7 @@ class GtkNotificationDaemonNotification extends MessageTray.Notification {
serialize() {
return this._serialized;
}
};
});
const FdoApplicationIface = loadInterfaceXML('org.freedesktop.Application');
const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
@ -618,9 +620,9 @@ function getPlatformData() {
function InvalidAppError() {}
var GtkNotificationDaemonAppSource =
var GtkNotificationDaemonAppSource = GObject.registerClass(
class GtkNotificationDaemonAppSource extends MessageTray.Source {
constructor(appId) {
_init(appId) {
let objectPath = objectPathFromAppId(appId);
if (!GLib.Variant.is_object_path(objectPath))
throw new InvalidAppError();
@ -629,7 +631,7 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source {
if (!app)
throw new InvalidAppError();
super(app.get_name());
super._init(app.get_name());
this._appId = appId;
this._app = app;
@ -690,7 +692,7 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source {
this._notifications[notificationId] = notification;
if (showBanner)
this.notify(notification);
this.showNotification(notification);
else
this.pushNotification(notification);
@ -716,7 +718,7 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source {
}
return [this._appId, notifications];
}
};
});
const GtkNotificationsIface = loadInterfaceXML('org.gtk.Notifications');
@ -742,7 +744,7 @@ var GtkNotificationDaemon = class GtkNotificationDaemon {
delete this._sources[appId];
this._saveNotifications();
});
source.connect('count-updated', this._saveNotifications.bind(this));
source.connect('notify::count', this._saveNotifications.bind(this));
Main.messageTray.add(source);
this._sources[appId] = source;
return source;

View File

@ -1,30 +1,33 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported OsdMonitorLabeler */
const { Clutter, Gio, Meta, St } = imports.gi;
const { Clutter, Gio, GObject, Meta, St } = imports.gi;
const Main = imports.ui.main;
var OsdMonitorLabel = class {
constructor(monitor, label) {
this._actor = new St.Widget({ x_expand: true,
y_expand: true });
var OsdMonitorLabel = GObject.registerClass(
class OsdMonitorLabel extends St.Widget {
_init(monitor, label) {
super._init({ x_expand: true, y_expand: true });
this._monitor = monitor;
this._box = new St.BoxLayout({ style_class: 'osd-window',
vertical: true });
this._actor.add_actor(this._box);
this.add_actor(this._box);
this._label = new St.Label({ style_class: 'osd-monitor-label',
text: label });
this._box.add(this._label);
Main.uiGroup.add_child(this._actor);
Main.uiGroup.set_child_above_sibling(this._actor, null);
Main.uiGroup.add_child(this);
Main.uiGroup.set_child_above_sibling(this, null);
this._position();
Meta.disable_unredirect_for_display(global.display);
this.connect('destroy', () => {
Meta.enable_unredirect_for_display(global.display);
});
}
_position() {
@ -37,12 +40,7 @@ var OsdMonitorLabel = class {
this._box.y = workArea.y;
}
destroy() {
this._actor.destroy();
Meta.enable_unredirect_for_display(global.display);
}
};
});
var OsdMonitorLabeler = class {
constructor() {
@ -68,7 +66,7 @@ var OsdMonitorLabeler = class {
_trackClient(client) {
if (this._client)
return (this._client == client);
return this._client == client;
this._client = client;
this._clientWatchId = Gio.bus_watch_name(Gio.BusType.SESSION, client, 0, null,

View File

@ -41,39 +41,42 @@ class OsdWindowConstraint extends Clutter.Constraint {
}
});
var OsdWindow = class {
constructor(monitorIndex) {
this.actor = new St.Widget({ x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER });
var OsdWindow = GObject.registerClass(
class OsdWindow extends St.Widget {
_init(monitorIndex) {
super._init({
x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
});
this._monitorIndex = monitorIndex;
let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
this.actor.add_constraint(constraint);
this.add_constraint(constraint);
this._boxConstraint = new OsdWindowConstraint();
this._box = new St.BoxLayout({ style_class: 'osd-window',
vertical: true });
this._box.add_constraint(this._boxConstraint);
this.actor.add_actor(this._box);
this.add_actor(this._box);
this._icon = new St.Icon();
this._box.add(this._icon, { expand: true });
this._icon = new St.Icon({ y_expand: true });
this._box.add_child(this._icon);
this._label = new St.Label();
this._box.add(this._label);
this._level = new BarLevel.BarLevel({
style_class: 'level',
value: 0
value: 0,
});
this._box.add(this._level);
this._hideTimeoutId = 0;
this._reset();
this.actor.connect('destroy', this._onDestroy.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
this._monitorsChangedId =
Main.layoutManager.connect('monitors-changed',
@ -83,7 +86,7 @@ var OsdWindow = class {
themeContext.connect('notify::scale-factor',
this._relayout.bind(this));
this._relayout();
Main.uiGroup.add_child(this.actor);
Main.uiGroup.add_child(this);
}
_onDestroy() {
@ -102,21 +105,22 @@ var OsdWindow = class {
}
setLabel(label) {
this._label.visible = (label != undefined);
this._label.visible = label != undefined;
if (label)
this._label.text = label;
}
setLevel(value) {
this._level.visible = (value != undefined);
this._level.visible = value != undefined;
if (value != undefined) {
if (this.actor.visible)
if (this.visible) {
this._level.ease_property('value', value, {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: LEVEL_ANIMATION_TIME
duration: LEVEL_ANIMATION_TIME,
});
else
} else {
this._level.value = value;
}
}
}
@ -128,16 +132,16 @@ var OsdWindow = class {
if (!this._icon.gicon)
return;
if (!this.actor.visible) {
if (!this.visible) {
Meta.disable_unredirect_for_display(global.display);
this.actor.show();
this.actor.opacity = 0;
this.actor.get_parent().set_child_above_sibling(this.actor, null);
super.show();
this.opacity = 0;
this.get_parent().set_child_above_sibling(this, null);
this.actor.ease({
this.ease({
opacity: 255,
duration: FADE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
@ -158,20 +162,20 @@ var OsdWindow = class {
_hide() {
this._hideTimeoutId = 0;
this.actor.ease({
this.ease({
opacity: 0,
duration: FADE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._reset();
Meta.enable_unredirect_for_display(global.display);
}
},
});
return GLib.SOURCE_REMOVE;
}
_reset() {
this.actor.hide();
super.hide();
this.setLabel(null);
this.setMaxLevel(null);
this.setLevel(null);
@ -193,7 +197,7 @@ var OsdWindow = class {
this._box.translation_y = Math.round(monitor.height / 4);
this._boxConstraint.minSize = popupSize;
}
};
});
var OsdWindowManager = class {
constructor() {
@ -210,7 +214,7 @@ var OsdWindowManager = class {
}
for (let i = Main.layoutManager.monitors.length; i < this._osdWindows.length; i++) {
this._osdWindows[i].actor.destroy();
this._osdWindows[i].destroy();
this._osdWindows[i] = null;
}

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Overview */
const { Clutter, GLib, Meta, Shell, St } = imports.gi;
const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
const Signals = imports.signals;
const Background = imports.ui.background;
@ -72,36 +72,109 @@ var ShellInfo = class {
if (undoCallback)
notification.addAction(_("Undo"), this._onUndoClicked.bind(this));
this._source.notify(notification);
this._source.showNotification(notification);
}
};
var OverviewActor = GObject.registerClass(
class OverviewActor extends St.BoxLayout {
_init() {
super._init({
name: 'overview',
/* Translators: This is the main view to select
activities. See also note for "Activities" string. */
accessible_name: _("Overview"),
vertical: true,
});
this.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
// Add a clone of the panel to the overview so spacing and such is
// automatic
let panelGhost = new St.Bin({
child: new Clutter.Clone({ source: Main.panel }),
reactive: false,
opacity: 0,
});
this.add_actor(panelGhost);
this._searchEntry = new St.Entry({
style_class: 'search-entry',
/* Translators: this is the text displayed
in the search entry when no search is
active; it should not exceed ~30
characters. */
hint_text: _("Type to search…"),
track_hover: true,
can_focus: true,
});
this._searchEntry.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
let searchEntryBin = new St.Bin({
child: this._searchEntry,
x_align: Clutter.ActorAlign.CENTER,
});
this.add_actor(searchEntryBin);
this._controls = new OverviewControls.ControlsManager(this._searchEntry);
// Add our same-line elements after the search entry
this.add_child(this._controls);
}
get dash() {
return this._controls.dash;
}
get searchEntry() {
return this._searchEntry;
}
get viewSelector() {
return this._controls.viewSelector;
}
});
var Overview = class {
constructor() {
this._overviewCreated = false;
this._initCalled = false;
Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
this._sessionUpdated();
}
get dash() {
return this._overview.dash;
}
get dashIconSize() {
logError(new Error('Usage of Overview.\'dashIconSize\' is deprecated, ' +
'use \'dash.iconSize\' property instead'));
return this.dash.iconSize;
}
get viewSelector() {
return this._overview.viewSelector;
}
get animationInProgress() {
return this._animationInProgress;
}
get visible() {
return this._visible;
}
get visibleTarget() {
return this._visibleTarget;
}
_createOverview() {
if (this._overviewCreated)
if (this._overview)
return;
if (this.isDummy)
return;
this._overviewCreated = true;
this._overview = new St.BoxLayout({ name: 'overview',
/* Translators: This is the main view to select
activities. See also note for "Activities" string. */
accessible_name: _("Overview"),
vertical: true });
this._overview.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
this._overview._delegate = this;
// The main Background actors are inside global.window_group which are
// hidden when displaying the overview, so we create a new
// one. Instances of this class share a single CoglTexture behind the
@ -116,11 +189,11 @@ var Overview = class {
this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out
this._visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide()
this._modal = false; // have a modal grab
this.animationInProgress = false;
this.visibleTarget = false;
this._animationInProgress = false;
this._visibleTarget = false;
// During transitions, we raise this to the top to avoid having the overview
// area be reactive; it causes too many issues such as double clicks on
@ -129,14 +202,11 @@ var Overview = class {
reactive: true });
Main.layoutManager.overviewGroup.add_child(this._coverPane);
this._coverPane.connect('event', () => Clutter.EVENT_STOP);
Main.layoutManager.overviewGroup.add_child(this._overview);
this._coverPane.hide();
// XDND
this._dragMonitor = {
dragMotion: this._onDragMotion.bind(this)
dragMotion: this._onDragMotion.bind(this),
};
@ -175,11 +245,11 @@ var Overview = class {
for (let i = 0; i < backgrounds.length; i++) {
backgrounds[i].ease_property('brightness', 1.0, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
backgrounds[i].ease_property('vignette-sharpness', 0.0, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
}
@ -189,11 +259,11 @@ var Overview = class {
for (let i = 0; i < backgrounds.length; i++) {
backgrounds[i].ease_property('brightness', Lightbox.VIGNETTE_BRIGHTNESS, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
backgrounds[i].ease_property('vignette-sharpness', Lightbox.VIGNETTE_SHARPNESS, {
duration: SHADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
}
@ -213,41 +283,12 @@ var Overview = class {
if (this.isDummy)
return;
this._overview = new OverviewActor();
this._overview._delegate = this;
Main.layoutManager.overviewGroup.add_child(this._overview);
this._shellInfo = new ShellInfo();
// Add a clone of the panel to the overview so spacing and such is
// automatic
this._panelGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.panel }),
reactive: false,
opacity: 0 });
this._overview.add_actor(this._panelGhost);
this._searchEntry = new St.Entry({ style_class: 'search-entry',
/* Translators: this is the text displayed
in the search entry when no search is
active; it should not exceed ~30
characters. */
hint_text: _("Type to search…"),
track_hover: true,
can_focus: true });
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
x_align: St.Align.MIDDLE });
this._overview.add_actor(this._searchEntryBin);
// Create controls
this._controls = new OverviewControls.ControlsManager(this._searchEntry);
this._dash = this._controls.dash;
this.viewSelector = this._controls.viewSelector;
// Add our same-line elements after the search entry
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
// TODO - recalculate everything when desktop size changes
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed', () => {
this.dashIconSize = this._dash.iconSize;
});
Main.layoutManager.connect('monitors-changed', this._relayout.bind(this));
this._relayout();
}
@ -426,7 +467,7 @@ var Overview = class {
focusSearch() {
this.show();
this._searchEntry.grab_key_focus();
this._overview.searchEntry.grab_key_focus();
}
fadeInDesktop() {
@ -435,7 +476,7 @@ var Overview = class {
this._desktopFade.ease({
opacity: 255,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: ANIMATION_TIME
duration: ANIMATION_TIME,
});
}
@ -453,7 +494,7 @@ var Overview = class {
this._desktopFade.ease({
opacity: 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: ANIMATION_TIME
duration: ANIMATION_TIME,
});
}
@ -464,11 +505,11 @@ var Overview = class {
// the overview if the user both triggered the hot corner and
// clicked the Activities button.
shouldToggleByCornerOrButton() {
if (this.animationInProgress)
if (this._animationInProgress)
return false;
if (this._inItemDrag || this._inWindowDrag)
return false;
if (this._activationTime == 0 ||
if (!this._activationTime ||
GLib.get_monotonic_time() / GLib.USEC_PER_SEC - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
return true;
return false;
@ -478,7 +519,7 @@ var Overview = class {
// We delay grab changes during animation so that when removing the
// overview we don't have a problem with the release of a press/release
// going to an application.
if (this.animationInProgress)
if (this._animationInProgress)
return true;
if (this._shown) {
@ -493,6 +534,7 @@ var Overview = class {
}
}
} else {
// eslint-disable-next-line no-lonely-if
if (this._modal) {
Main.popModal(this._overview);
this._modal = false;
@ -520,12 +562,12 @@ var Overview = class {
_animateVisible() {
if (this.visible || this.animationInProgress)
if (this._visible || this._animationInProgress)
return;
this.visible = true;
this.animationInProgress = true;
this.visibleTarget = true;
this._visible = true;
this._animationInProgress = true;
this._visibleTarget = true;
this._activationTime = GLib.get_monotonic_time() / GLib.USEC_PER_SEC;
Meta.disable_unredirect_for_display(global.display);
@ -536,17 +578,18 @@ var Overview = class {
opacity: 255,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: ANIMATION_TIME,
onComplete: () => this._showDone()
onComplete: () => this._showDone(),
});
this._shadeBackgrounds();
this._coverPane.raise_top();
Main.layoutManager.overviewGroup.set_child_above_sibling(
this._coverPane, null);
this._coverPane.show();
this.emit('showing');
}
_showDone() {
this.animationInProgress = false;
this._animationInProgress = false;
this._desktopFade.hide();
this._coverPane.hide();
@ -572,8 +615,8 @@ var Overview = class {
let event = Clutter.get_current_event();
if (event) {
let type = event.type();
let button = (type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE);
let button = type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE;
let ctrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
if (button && ctrl)
return;
@ -586,11 +629,11 @@ var Overview = class {
}
_animateNotVisible() {
if (!this.visible || this.animationInProgress)
if (!this._visible || this._animationInProgress)
return;
this.animationInProgress = true;
this.visibleTarget = false;
this._animationInProgress = true;
this._visibleTarget = false;
this.viewSelector.animateFromOverview();
@ -599,11 +642,12 @@ var Overview = class {
opacity: 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: ANIMATION_TIME,
onComplete: () => this._hideDone()
onComplete: () => this._hideDone(),
});
this._unshadeBackgrounds();
this._coverPane.raise_top();
Main.layoutManager.overviewGroup.set_child_above_sibling(
this._coverPane, null);
this._coverPane.show();
this.emit('hiding');
}
@ -616,8 +660,8 @@ var Overview = class {
this._desktopFade.hide();
this._coverPane.hide();
this.visible = false;
this.animationInProgress = false;
this._visible = false;
this._animationInProgress = false;
this.emit('hidden');
// Handle any calls to show* while we were hiding
@ -633,14 +677,17 @@ var Overview = class {
if (this.isDummy)
return;
if (this.visible)
if (this._visible)
this.hide();
else
this.show();
}
getShowAppsButton() {
return this._dash.showAppsButton;
logError(new Error('Usage of Overview.\'getShowAppsButton\' is deprecated, ' +
'use \'dash.showAppsButton\' property instead'));
return this.dash.showAppsButton;
}
};
Signals.addSignalMethods(Overview.prototype);

View File

@ -12,17 +12,17 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
var SIDE_CONTROLS_ANIMATION_TIME = 160;
function getRtlSlideDirection(direction, actor) {
let rtl = (actor.text_direction == Clutter.TextDirection.RTL);
if (rtl)
direction = (direction == SlideDirection.LEFT)
let rtl = actor.text_direction == Clutter.TextDirection.RTL;
if (rtl) {
direction = direction == SlideDirection.LEFT
? SlideDirection.RIGHT : SlideDirection.LEFT;
}
return direction;
}
var SlideDirection = {
LEFT: 0,
RIGHT: 1
RIGHT: 1,
};
var SlideLayout = GObject.registerClass({
@ -34,8 +34,8 @@ var SlideLayout = GObject.registerClass({
'translation-x': GObject.ParamSpec.double(
'translation-x', 'translation-x', 'translation-x',
GObject.ParamFlags.READWRITE,
-Infinity, Infinity, 0)
}
-Infinity, Infinity, 0),
},
}, class SlideLayout extends Clutter.FixedLayout {
_init(params) {
this._slideX = 1;
@ -67,7 +67,7 @@ var SlideLayout = GObject.registerClass({
// flags only determine what to do if the allocated box is bigger
// than the actor's box.
let realDirection = getRtlSlideDirection(this._direction, child);
let alignX = (realDirection == SlideDirection.LEFT)
let alignX = realDirection == SlideDirection.LEFT
? availWidth - natWidth
: availWidth - natWidth * this._slideX;
@ -118,18 +118,21 @@ var SlideLayout = GObject.registerClass({
}
});
var SlidingControl = class {
constructor(params) {
var SlidingControl = GObject.registerClass(
class SlidingControl extends St.Widget {
_init(params) {
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
this._visible = true;
this._inDrag = false;
this.layout = new SlideLayout();
this.layout.slideDirection = params.slideDirection;
this.actor = new St.Widget({ layout_manager: this.layout,
style_class: 'overview-controls',
clip_to_allocation: true });
super._init({
layout_manager: this.layout,
style_class: 'overview-controls',
clip_to_allocation: true,
});
this._visible = true;
this._inDrag = false;
Main.overview.connect('hiding', this._onOverviewHiding.bind(this));
@ -147,25 +150,25 @@ var SlidingControl = class {
}
_updateSlide() {
this.actor.ease_property('@layout.slide-x', this._getSlide(), {
this.ease_property('@layout.slide-x', this._getSlide(), {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: SIDE_CONTROLS_ANIMATION_TIME,
});
}
getVisibleWidth() {
let child = this.actor.get_first_child();
let child = this.get_first_child();
let [, , natWidth] = child.get_preferred_size();
return natWidth;
}
_getTranslation() {
let child = this.actor.get_first_child();
let child = this.get_first_child();
let direction = getRtlSlideDirection(this.layout.slideDirection, child);
let visibleWidth = this.getVisibleWidth();
if (direction == SlideDirection.LEFT)
return - visibleWidth;
return -visibleWidth;
else
return visibleWidth;
}
@ -175,18 +178,17 @@ var SlidingControl = class {
let translationEnd = 0;
let translation = this._getTranslation();
let shouldShow = (this._getSlide() > 0);
if (shouldShow) {
let shouldShow = this._getSlide() > 0;
if (shouldShow)
translationStart = translation;
} else {
else
translationEnd = translation;
}
if (this.layout.translation_x == translationEnd)
return;
this.layout.translation_x = translationStart;
this.actor.ease_property('@layout.translation-x', translationEnd, {
this.ease_property('@layout.translation-x', translationEnd, {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: SIDE_CONTROLS_ANIMATION_TIME,
});
@ -218,18 +220,18 @@ var SlidingControl = class {
}
fadeIn() {
this.actor.ease({
this.ease({
opacity: 255,
duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
mode: Clutter.AnimationMode.EASE_IN_QUAD
mode: Clutter.AnimationMode.EASE_IN_QUAD,
});
}
fadeHalf() {
this.actor.ease({
this.ease({
opacity: 128,
duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
@ -249,37 +251,38 @@ var SlidingControl = class {
// selector; this means we can now safely set the full slide for
// the next page, since slideIn or slideOut might have been called,
// changing the visiblity
this.actor.remove_transition('@layout.slide-x');
this.remove_transition('@layout.slide-x');
this.layout.slide_x = this._getSlide();
this._updateTranslation();
}
};
});
var ThumbnailsSlider = class extends SlidingControl {
constructor(thumbnailsBox) {
super({ slideDirection: SlideDirection.RIGHT });
var ThumbnailsSlider = GObject.registerClass(
class ThumbnailsSlider extends SlidingControl {
_init(thumbnailsBox) {
super._init({ slideDirection: SlideDirection.RIGHT });
this._thumbnailsBox = thumbnailsBox;
this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
this.actor.reactive = true;
this.actor.track_hover = true;
this.actor.add_actor(this._thumbnailsBox);
this.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
this.reactive = true;
this.track_hover = true;
this.add_actor(this._thumbnailsBox);
Main.layoutManager.connect('monitors-changed', this._updateSlide.bind(this));
global.workspace_manager.connect('active-workspace-changed',
this._updateSlide.bind(this));
global.workspace_manager.connect('notify::n-workspaces',
this._updateSlide.bind(this));
this.actor.connect('notify::hover', this._updateSlide.bind(this));
this._thumbnailsBox.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
this.connect('notify::hover', this._updateSlide.bind(this));
this._thumbnailsBox.bind_property('visible', this, 'visible', GObject.BindingFlags.SYNC_CREATE);
}
_getAlwaysZoomOut() {
// Always show the pager on hover, during a drag, or if workspaces are
// actually used, e.g. there are windows on any non-active workspace
let workspaceManager = global.workspace_manager;
let alwaysZoomOut = this.actor.hover ||
let alwaysZoomOut = this.hover ||
this._inDrag ||
!Meta.prefs_get_dynamic_workspaces() ||
workspaceManager.n_workspaces > 2 ||
@ -304,12 +307,12 @@ var ThumbnailsSlider = class extends SlidingControl {
}
getNonExpandedWidth() {
let child = this.actor.get_first_child();
let child = this.get_first_child();
return child.get_theme_node().get_length('visible-width');
}
_onDragEnd() {
this.actor.sync_hover();
this.sync_hover();
super._onDragEnd();
}
@ -321,7 +324,7 @@ var ThumbnailsSlider = class extends SlidingControl {
if (alwaysZoomOut)
return 1;
let child = this.actor.get_first_child();
let child = this.get_first_child();
let preferredHeight = child.get_preferred_height(-1)[1];
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
@ -335,24 +338,25 @@ var ThumbnailsSlider = class extends SlidingControl {
else
return this.getNonExpandedWidth();
}
};
});
var DashSlider = class extends SlidingControl {
constructor(dash) {
super({ slideDirection: SlideDirection.LEFT });
var DashSlider = GObject.registerClass(
class DashSlider extends SlidingControl {
_init(dash) {
super._init({ slideDirection: SlideDirection.LEFT });
this._dash = dash;
// SlideLayout reads the actor's expand flags to decide
// whether to allocate the natural size to its child, or the whole
// available allocation
this._dash.actor.x_expand = true;
this._dash.x_expand = true;
this.actor.x_expand = true;
this.actor.x_align = Clutter.ActorAlign.START;
this.actor.y_expand = true;
this.x_expand = true;
this.x_align = Clutter.ActorAlign.START;
this.y_expand = true;
this.actor.add_actor(this._dash.actor);
this.add_actor(this._dash);
this._dash.connect('icon-size-changed', this._updateSlide.bind(this));
}
@ -371,7 +375,7 @@ var DashSlider = class extends SlidingControl {
_onWindowDragEnd() {
this.fadeIn();
}
};
});
var DashSpacer = GObject.registerClass(
class DashSpacer extends St.Widget {
@ -416,12 +420,21 @@ var ControlsLayout = GObject.registerClass({
}
});
var ControlsManager = class {
constructor(searchEntry) {
var ControlsManager = GObject.registerClass(
class ControlsManager extends St.Widget {
_init(searchEntry) {
let layout = new ControlsLayout();
super._init({
layout_manager: layout,
x_expand: true,
y_expand: true,
clip_to_allocation: true,
});
this.dash = new Dash.Dash();
this._dashSlider = new DashSlider(this.dash);
this._dashSpacer = new DashSpacer();
this._dashSpacer.setDashActor(this._dashSlider.actor);
this._dashSpacer.setDashActor(this._dashSlider);
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
@ -431,20 +444,15 @@ var ControlsManager = class {
this.viewSelector.connect('page-changed', this._setVisibility.bind(this));
this.viewSelector.connect('page-empty', this._onPageEmpty.bind(this));
let layout = new ControlsLayout();
this.actor = new St.Widget({ layout_manager: layout,
x_expand: true, y_expand: true,
clip_to_allocation: true });
this._group = new St.BoxLayout({ name: 'overview-group',
x_expand: true, y_expand: true });
this.actor.add_actor(this._group);
this.add_actor(this._group);
this.actor.add_actor(this._dashSlider.actor);
this.add_actor(this._dashSlider);
this._group.add_actor(this._dashSpacer);
this._group.add(this.viewSelector.actor, { x_fill: true,
expand: true });
this._group.add_actor(this._thumbnailsSlider.actor);
this._group.add_child(this.viewSelector);
this._group.add_actor(this._thumbnailsSlider);
layout.connect('allocation-changed', this._updateWorkspacesGeometry.bind(this));
@ -452,18 +460,18 @@ var ControlsManager = class {
}
_updateWorkspacesGeometry() {
let [x, y] = this.actor.get_transformed_position();
let [width, height] = this.actor.get_transformed_size();
let geometry = { x: x, y: y, width: width, height: height };
let [x, y] = this.get_transformed_position();
let [width, height] = this.get_transformed_size();
let geometry = { x, y, width, height };
let spacing = this.actor.get_theme_node().get_length('spacing');
let spacing = this.get_theme_node().get_length('spacing');
let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
geometry.width -= dashWidth;
geometry.width -= thumbnailsWidth;
if (this.actor.get_text_direction() == Clutter.TextDirection.LTR)
if (this.get_text_direction() == Clutter.TextDirection.LTR)
geometry.x += dashWidth;
else
geometry.x += thumbnailsWidth;
@ -481,9 +489,9 @@ var ControlsManager = class {
return;
let activePage = this.viewSelector.getActivePage();
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
activePage == ViewSelector.ViewPage.APPS);
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
let dashVisible = activePage == ViewSelector.ViewPage.WINDOWS ||
activePage == ViewSelector.ViewPage.APPS;
let thumbnailsVisible = activePage == ViewSelector.ViewPage.WINDOWS;
if (dashVisible)
this._dashSlider.slideIn();
@ -501,7 +509,7 @@ var ControlsManager = class {
return;
let activePage = this.viewSelector.getActivePage();
this._dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
this._dashSpacer.visible = activePage == ViewSelector.ViewPage.WINDOWS;
}
_onPageEmpty() {
@ -510,4 +518,4 @@ var ControlsManager = class {
this._updateSpacerVisibility();
}
};
});

View File

@ -1,5 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported PadOsdService */
/* exported PadOsd, PadOsdService */
const { Atk, Clutter, GDesktopEnums, Gio,
GLib, GObject, Gtk, Meta, Rsvg, St } = imports.gi;
@ -22,40 +22,45 @@ const CCW = 1;
const UP = 0;
const DOWN = 1;
var PadChooser = class {
constructor(device, groupDevices) {
this.actor = new St.Button({ style_class: 'pad-chooser-button',
toggle_mode: true,
x_fill: false,
y_fill: false,
x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
var PadChooser = GObject.registerClass({
Signals: { 'pad-selected': { param_types: [Clutter.InputDevice.$gtype] } },
}, class PadChooser extends St.Button {
_init(device, groupDevices) {
super._init({
style_class: 'pad-chooser-button',
toggle_mode: true,
});
this.currentDevice = device;
this._padChooserMenu = null;
let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
icon_name: 'pan-down-symbolic',
accessible_role: Atk.Role.ARROW });
this.actor.set_child(arrow);
let arrow = new St.Icon({
style_class: 'popup-menu-arrow',
icon_name: 'pan-down-symbolic',
accessible_role: Atk.Role.ARROW,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
});
this.set_child(arrow);
this._ensureMenu(groupDevices);
this.actor.connect('destroy', this._onDestroy.bind(this));
this.actor.connect('clicked', actor => {
if (actor.get_checked()) {
if (this._padChooserMenu != null)
this._padChooserMenu.open(true);
else
this.set_checked(false);
} else {
this._padChooserMenu.close(true);
}
});
this.connect('destroy', this._onDestroy.bind(this));
}
vfunc_clicked() {
if (this.get_checked()) {
if (this._padChooserMenu != null)
this._padChooserMenu.open(true);
else
this.set_checked(false);
} else {
this._padChooserMenu.close(true);
}
}
_ensureMenu(devices) {
this._padChooserMenu = new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.TOP);
this._padChooserMenu = new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP);
this._padChooserMenu.connect('menu-closed', () => {
this.actor.set_checked(false);
this.set_checked(false);
});
this._padChooserMenu.actor.hide();
Main.uiGroup.add_actor(this._padChooserMenu.actor);
@ -78,24 +83,19 @@ var PadChooser = class {
update(devices) {
if (this._padChooserMenu)
this._padChooserMenu.actor.destroy();
this.actor.set_checked(false);
this.set_checked(false);
this._ensureMenu(devices);
}
});
destroy() {
this.actor.destroy();
}
};
Signals.addSignalMethods(PadChooser.prototype);
var KeybindingEntry = class {
constructor() {
this.actor = new St.Entry({ hint_text: _("New shortcut…"),
style: 'width: 10em' });
this.actor.connect('captured-event', this._onCapturedEvent.bind(this));
var KeybindingEntry = GObject.registerClass({
Signals: { 'keybinding-edited': {} },
}, class KeybindingEntry extends St.Entry {
_init() {
super._init({ hint_text: _("New shortcut…"), style: 'width: 10em' });
}
_onCapturedEvent(actor, event) {
vfunc_captured_event(event) {
if (event.type() != Clutter.EventType.KEY_PRESS)
return Clutter.EVENT_PROPAGATE;
@ -103,23 +103,23 @@ var KeybindingEntry = class {
event.get_key_symbol(),
event.get_key_code(),
event.get_state());
this.actor.set_text(str);
this.set_text(str);
this.emit('keybinding-edited', str);
return Clutter.EVENT_STOP;
}
};
Signals.addSignalMethods(KeybindingEntry.prototype);
});
var ActionComboBox = class {
constructor() {
this.actor = new St.Button({ style_class: 'button' });
this.actor.connect('clicked', this._onButtonClicked.bind(this));
this.actor.set_toggle_mode(true);
var ActionComboBox = GObject.registerClass({
Signals: { 'action-selected': { param_types: [GObject.TYPE_INT] } },
}, class ActionComboBox extends St.Button {
_init() {
super._init({ style_class: 'button' });
this.set_toggle_mode(true);
let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL,
spacing: 6 });
let box = new St.Widget({ layout_manager: boxLayout });
this.actor.set_child(box);
this.set_child(box);
this._label = new St.Label({ style_class: 'combo-box-label' });
box.add_child(this._label);
@ -131,9 +131,9 @@ var ActionComboBox = class {
y_align: Clutter.ActorAlign.CENTER });
box.add_child(arrow);
this._editMenu = new PopupMenu.PopupMenu(this.actor, 0, St.Side.TOP);
this._editMenu = new PopupMenu.PopupMenu(this, 0, St.Side.TOP);
this._editMenu.connect('menu-closed', () => {
this.actor.set_checked(false);
this.set_checked(false);
});
this._editMenu.actor.hide();
Main.uiGroup.add_actor(this._editMenu.actor);
@ -179,8 +179,8 @@ var ActionComboBox = class {
this._editMenu.close(true);
}
_onButtonClicked() {
if (this.actor.get_checked())
vfunc_clicked() {
if (this.get_checked())
this.popup();
else
this.popdown();
@ -189,38 +189,39 @@ var ActionComboBox = class {
setButtonActionsActive(active) {
this._buttonItems.forEach(item => item.setSensitive(active));
}
};
Signals.addSignalMethods(ActionComboBox.prototype);
});
var ActionEditor = class {
constructor() {
var ActionEditor = GObject.registerClass({
Signals: { 'done': {} },
}, class ActionEditor extends St.Widget {
_init() {
let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL,
spacing: 12 });
this.actor = new St.Widget({ layout_manager: boxLayout });
super._init({ layout_manager: boxLayout });
this._actionComboBox = new ActionComboBox();
this._actionComboBox.connect('action-selected', this._onActionSelected.bind(this));
this.actor.add_actor(this._actionComboBox.actor);
this.add_actor(this._actionComboBox);
this._keybindingEdit = new KeybindingEntry();
this._keybindingEdit.connect('keybinding-edited', this._onKeybindingEdited.bind(this));
this.actor.add_actor(this._keybindingEdit.actor);
this.add_actor(this._keybindingEdit);
this._doneButton = new St.Button({ label: _("Done"),
style_class: 'button',
x_expand: false });
this._doneButton.connect('clicked', this._onEditingDone.bind(this));
this.actor.add_actor(this._doneButton);
this.add_actor(this._doneButton);
}
_updateKeybindingEntryState() {
if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) {
this._keybindingEdit.actor.set_text(this._currentKeybinding);
this._keybindingEdit.actor.show();
this._keybindingEdit.actor.grab_key_focus();
this._keybindingEdit.set_text(this._currentKeybinding);
this._keybindingEdit.show();
this._keybindingEdit.grab_key_focus();
} else {
this._keybindingEdit.actor.hide();
this._keybindingEdit.hide();
}
}
@ -232,13 +233,13 @@ var ActionEditor = class {
this._actionComboBox.setAction(this._currentAction);
this._updateKeybindingEntryState();
let isButton = (action == Meta.PadActionType.BUTTON);
let isButton = action == Meta.PadActionType.BUTTON;
this._actionComboBox.setButtonActionsActive(isButton);
}
close() {
this._actionComboBox.popdown();
this.actor.hide();
this.hide();
}
_onKeybindingEdited(entry, keybinding) {
@ -272,8 +273,7 @@ var ActionEditor = class {
this.close();
this.emit('done');
}
};
Signals.addSignalMethods(ActionEditor.prototype);
});
var PadDiagram = GObject.registerClass({
Properties: {
@ -291,7 +291,7 @@ var PadDiagram = GObject.registerClass({
'Editor actor',
GObject.ParamFlags.READWRITE |
GObject.ParamFlags.CONSTRUCT_ONLY,
Clutter.Actor.$gtype)
Clutter.Actor.$gtype),
},
}, class PadDiagram extends St.DrawingArea {
_init(params) {
@ -344,17 +344,19 @@ var PadDiagram = GObject.registerClass({
}
_wrappingSvgHeader() {
return ('<?xml version="1.0" encoding="UTF-8" standalone="no"?>' +
'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
'xmlns:xi="http://www.w3.org/2001/XInclude" ' +
`width="${this._imageWidth}" height="${this._imageHeight}"> ` +
'<style type="text/css">');
return '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' +
'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
'xmlns:xi="http://www.w3.org/2001/XInclude" ' +
`width="${ // " (give xgettext the paired quotes it expects)
this._imageWidth
}" height="${this._imageHeight}"> ` + // "
'<style type="text/css">';
}
_wrappingSvgFooter() {
return ('</style>' +
return '</style>' +
'<xi:include href="' + this._imagePath + '" />' +
'</svg>');
'</svg>';
}
_cssString() {
@ -615,8 +617,21 @@ var PadDiagram = GObject.registerClass({
}
});
var PadOsd = class {
constructor(padDevice, settings, imagePath, editionMode, monitorIndex) {
var PadOsd = GObject.registerClass({
Signals: {
'pad-selected': { param_types: [Clutter.InputDevice.$gtype] },
'closed': {},
},
}, class PadOsd extends St.BoxLayout {
_init(padDevice, settings, imagePath, editionMode, monitorIndex) {
super._init({
style_class: 'pad-osd-window',
vertical: true,
x_expand: true,
y_expand: true,
reactive: true,
});
this.padDevice = padDevice;
this._groupPads = [padDevice];
this._settings = settings;
@ -653,23 +668,18 @@ var PadOsd = class {
this._groupPads.push(device);
});
this.actor = new St.BoxLayout({ style_class: 'pad-osd-window',
x_expand: true,
y_expand: true,
vertical: true,
reactive: true });
this.actor.connect('destroy', this._onDestroy.bind(this));
Main.uiGroup.add_actor(this.actor);
this.connect('destroy', this._onDestroy.bind(this));
Main.uiGroup.add_actor(this);
this._monitorIndex = monitorIndex;
let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
this.actor.add_constraint(constraint);
this.add_constraint(constraint);
this._titleBox = new St.BoxLayout({ style_class: 'pad-osd-title-box',
vertical: false,
x_expand: false,
x_align: Clutter.ActorAlign.CENTER });
this.actor.add_actor(this._titleBox);
this.add_actor(this._titleBox);
let labelBox = new St.BoxLayout({ style_class: 'pad-osd-title-menu-box',
vertical: true });
@ -690,10 +700,10 @@ var PadOsd = class {
this._padDiagram = new PadDiagram({ image: this._imagePath,
left_handed: settings.get_boolean('left-handed'),
editor_actor: this._actionEditor.actor,
editor_actor: this._actionEditor,
x_expand: true,
y_expand: true });
this.actor.add_actor(this._padDiagram);
this.add_actor(this._padDiagram);
// FIXME: Fix num buttons.
let i = 0;
@ -724,18 +734,20 @@ var PadOsd = class {
x_expand: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER });
this.actor.add_actor(buttonBox);
this._editButton = new St.Button({ label: _("Edit…"),
style_class: 'button',
x_align: Clutter.ActorAlign.CENTER,
can_focus: true });
this.add_actor(buttonBox);
this._editButton = new St.Button({
label: _('Edit…'),
style_class: 'button',
can_focus: true,
x_align: Clutter.ActorAlign.CENTER,
});
this._editButton.connect('clicked', () => {
this.setEditionMode(true);
});
buttonBox.add_actor(this._editButton);
this._syncEditionMode();
Main.pushModal(this.actor);
Main.pushModal(this);
}
_updatePadChooser() {
@ -745,7 +757,7 @@ var PadOsd = class {
this._padChooser.connect('pad-selected', (chooser, pad) => {
this._requestForOtherPad(pad);
});
this._titleBox.add_child(this._padChooser.actor);
this._titleBox.add_child(this._padChooser);
} else {
this._padChooser.update(this._groupPads);
}
@ -785,7 +797,7 @@ var PadOsd = class {
this._padDiagram.deactivateButton(event.get_button());
return Clutter.EVENT_STOP;
} else if (event.type() == Clutter.EventType.KEY_PRESS &&
(!this._editionMode || event.get_key_symbol() == Clutter.Escape)) {
(!this._editionMode || event.get_key_symbol() === Clutter.KEY_Escape)) {
if (this._editedAction != null)
this._endActionEdition();
else
@ -832,22 +844,23 @@ var PadOsd = class {
this._tipLabel.set_text(_("Press any key to exit"));
}
this._titleLabel.clutter_text.set_markup('<span size="larger"><b>' + title + '</b></span>');
this._titleLabel.clutter_text.set_markup(
`<span size="larger"><b>${title}</b></span>`);
}
_isEditedAction(type, number, dir) {
if (!this._editedAction)
return false;
return (this._editedAction.type == type &&
return this._editedAction.type == type &&
this._editedAction.number == number &&
this._editedAction.dir == dir);
this._editedAction.dir == dir;
}
_followUpActionEdition(str) {
let { type, dir, number, mode } = this._editedAction;
let hasNextAction = (type == Meta.PadActionType.RING && dir == CCW ||
type == Meta.PadActionType.STRIP && dir == UP);
let hasNextAction = type == Meta.PadActionType.RING && dir == CCW ||
type == Meta.PadActionType.STRIP && dir == UP;
if (!hasNextAction)
return false;
@ -918,12 +931,8 @@ var PadOsd = class {
this._syncEditionMode();
}
destroy() {
this.actor.destroy();
}
_onDestroy() {
Main.popModal(this.actor);
Main.popModal(this);
this._actionEditor.close();
let deviceManager = Clutter.DeviceManager.get_default();
@ -941,11 +950,9 @@ var PadOsd = class {
this._capturedEventId = 0;
}
this.actor = null;
this.emit('closed');
}
};
Signals.addSignalMethods(PadOsd.prototype);
});
const PadOsdIface = loadInterfaceXML('org.gnome.Shell.Wacom.PadOsd');

View File

@ -1,10 +1,15 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported PageIndicators, AnimatedPageIndicators */
const { Clutter, GLib, GObject, Meta, St } = imports.gi;
const { Clutter, GLib, Graphene, GObject, Meta, St } = imports.gi;
const { ANIMATION_TIME_OUT, ANIMATION_MAX_DELAY_OUT_FOR_ITEM, AnimationDirection } = imports.ui.iconGrid;
const INDICATOR_INACTIVE_OPACITY = 128;
const INDICATOR_INACTIVE_OPACITY_HOVER = 255;
const INDICATOR_INACTIVE_SCALE = 2 / 3;
const INDICATOR_INACTIVE_SCALE_PRESSED = 0.5;
var INDICATORS_BASE_TIME = 250;
var INDICATORS_BASE_TIME_OUT = 125;
var INDICATORS_ANIMATION_DELAY = 125;
@ -12,24 +17,27 @@ var INDICATORS_ANIMATION_DELAY_OUT = 62.5;
var INDICATORS_ANIMATION_MAX_TIME = 750;
var SWITCH_TIME = 400;
var INDICATORS_ANIMATION_MAX_TIME_OUT =
Math.min (SWITCH_TIME,
ANIMATION_TIME_OUT + ANIMATION_MAX_DELAY_OUT_FOR_ITEM);
Math.min(SWITCH_TIME,
ANIMATION_TIME_OUT + ANIMATION_MAX_DELAY_OUT_FOR_ITEM);
var ANIMATION_DELAY = 100;
var PageIndicators = GObject.registerClass({
Signals: { 'page-activated': { param_types: [GObject.TYPE_INT] } }
Signals: { 'page-activated': { param_types: [GObject.TYPE_INT] } },
}, class PageIndicators extends St.BoxLayout {
_init(vertical = true) {
super._init({ style_class: 'page-indicators',
vertical,
x_expand: true, y_expand: true,
x_align: vertical ? Clutter.ActorAlign.END : Clutter.ActorAlign.CENTER,
y_align: vertical ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.END,
reactive: true,
clip_to_allocation: true });
_init(orientation = Clutter.Orientation.VERTICAL) {
let vertical = orientation == Clutter.Orientation.VERTICAL;
super._init({
style_class: 'page-indicators',
vertical,
x_expand: true, y_expand: true,
x_align: vertical ? Clutter.ActorAlign.END : Clutter.ActorAlign.CENTER,
y_align: vertical ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.END,
reactive: true,
clip_to_allocation: true,
});
this._nPages = 0;
this._currentPage = undefined;
this._currentPosition = 0;
this._reactive = true;
this._reactive = true;
}
@ -63,13 +71,21 @@ var PageIndicators = GObject.registerClass({
button_mask: St.ButtonMask.ONE |
St.ButtonMask.TWO |
St.ButtonMask.THREE,
toggle_mode: true,
reactive: this._reactive,
checked: pageIndex == this._currentPage });
indicator.child = new St.Widget({ style_class: 'page-indicator-icon' });
reactive: this._reactive });
indicator.child = new St.Widget({
style_class: 'page-indicator-icon',
pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
});
indicator.connect('clicked', () => {
this.emit('page-activated', pageIndex);
});
indicator.connect('notify::hover', () => {
this._updateIndicator(indicator, pageIndex);
});
indicator.connect('notify::pressed', () => {
this._updateIndicator(indicator, pageIndex);
});
this._updateIndicator(indicator, pageIndex);
this.add_actor(indicator);
}
} else {
@ -78,33 +94,57 @@ var PageIndicators = GObject.registerClass({
children[i].destroy();
}
this._nPages = nPages;
this.visible = (this._nPages > 1);
this.visible = this._nPages > 1;
}
setCurrentPage(currentPage) {
this._currentPage = currentPage;
_updateIndicator(indicator, pageIndex) {
let progress =
Math.max(1 - Math.abs(this._currentPosition - pageIndex), 0);
let inactiveScale = indicator.pressed
? INDICATOR_INACTIVE_SCALE_PRESSED : INDICATOR_INACTIVE_SCALE;
let inactiveOpacity = indicator.hover
? INDICATOR_INACTIVE_OPACITY_HOVER : INDICATOR_INACTIVE_OPACITY;
let scale = inactiveScale + (1 - inactiveScale) * progress;
let opacity = inactiveOpacity + (255 - inactiveOpacity) * progress;
indicator.child.set_scale(scale, scale);
indicator.child.opacity = opacity;
}
setCurrentPosition(currentPosition) {
this._currentPosition = currentPosition;
let children = this.get_children();
for (let i = 0; i < children.length; i++)
children[i].set_checked(i == this._currentPage);
this._updateIndicator(children[i], i);
}
});
var AnimatedPageIndicators = GObject.registerClass(
class AnimatedPageIndicators extends PageIndicators {
_init() {
super._init(true);
super._init();
this.connect('destroy', this._onDestroy.bind(this));
}
this.connect('notify::mapped', () => {
if (!this.mapped)
return;
_onDestroy() {
if (this.animateLater) {
Meta.later_remove(this.animateLater);
this.animateLater = 0;
}
}
// Implicit animations are skipped for unmapped actors, and our
// children aren't mapped yet, so defer to a later handler
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
this.animateIndicators(AnimationDirection.IN);
return GLib.SOURCE_REMOVE;
});
vfunc_map() {
super.vfunc_map();
// Implicit animations are skipped for unmapped actors, and our
// children aren't mapped yet, so defer to a later handler
this.animateLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
this.animateLater = 0;
this.animateIndicators(AnimationDirection.IN);
return GLib.SOURCE_REMOVE;
});
}
@ -143,7 +183,7 @@ class AnimatedPageIndicators extends PageIndicators {
translation_x: isAnimationIn ? 0 : offset,
duration: baseTime + delay * i,
mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
delay: isAnimationIn ? ANIMATION_DELAY : 0
delay: isAnimationIn ? ANIMATION_DELAY : 0,
});
}
}

View File

@ -57,8 +57,7 @@ function _unpremultiply(color) {
let red = Math.min((color.red * 255 + 127) / color.alpha, 255);
let green = Math.min((color.green * 255 + 127) / color.alpha, 255);
let blue = Math.min((color.blue * 255 + 127) / color.alpha, 255);
return new Clutter.Color({ red: red, green: green,
blue: blue, alpha: color.alpha });
return new Clutter.Color({ red, green, blue, alpha: color.alpha });
}
class AppMenu extends PopupMenu.PopupMenu {
@ -119,7 +118,7 @@ class AppMenu extends PopupMenu.PopupMenu {
_updateDetailsVisibility() {
let sw = this._appSystem.lookup_app('org.gnome.Software.desktop');
this._detailsItem.visible = (sw != null);
this._detailsItem.visible = sw != null;
}
isEmpty() {
@ -170,9 +169,13 @@ class AppMenu extends PopupMenu.PopupMenu {
let windows = this._app.get_windows();
windows.forEach(window => {
let title = window.title || this._app.get_name();
this._windowSection.addAction(title, event => {
let item = this._windowSection.addAction(title, event => {
Main.activateWindow(window, event.get_time());
});
let id = window.connect('notify::title', () => {
item.label.text = window.title || this._app.get_name();
});
item.connect('destroy', () => window.disconnect(id));
});
}
}
@ -234,8 +237,11 @@ var AppMenuButton = GObject.registerClass({
this._overviewHidingId = Main.overview.connect('hiding', this._sync.bind(this));
this._overviewShowingId = Main.overview.connect('showing', this._sync.bind(this));
this._spinner = new Animation.Spinner(PANEL_ICON_SIZE, true);
this._container.add_actor(this._spinner.actor);
this._spinner = new Animation.Spinner(PANEL_ICON_SIZE, {
animate: true,
hideOnStop: true,
});
this._container.add_actor(this._spinner);
let menu = new AppMenu(this);
this.setMenu(menu);
@ -264,7 +270,7 @@ var AppMenuButton = GObject.registerClass({
this.ease({
opacity: 255,
duration: Overview.ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
@ -279,7 +285,7 @@ var AppMenuButton = GObject.registerClass({
opacity: 0,
mode: Clutter.Animation.EASE_OUT_QUAD,
duration: Overview.ANIMATION_TIME,
onComplete: () => this.hide()
onComplete: () => this.hide(),
});
}
@ -340,9 +346,10 @@ var AppMenuButton = GObject.registerClass({
if (focusedApp && focusedApp.is_on_workspace(workspace))
return focusedApp;
for (let i = 0; i < this._startingApps.length; i++)
for (let i = 0; i < this._startingApps.length; i++) {
if (this._startingApps[i].is_on_workspace(workspace))
return this._startingApps[i];
}
return null;
}
@ -365,21 +372,21 @@ var AppMenuButton = GObject.registerClass({
}
}
let visible = (this._targetApp != null && !Main.overview.visibleTarget);
let visible = this._targetApp != null && !Main.overview.visibleTarget;
if (visible)
this.fadeIn();
else
this.fadeOut();
let isBusy = (this._targetApp != null &&
let isBusy = this._targetApp != null &&
(this._targetApp.get_state() == Shell.AppState.STARTING ||
this._targetApp.get_busy()));
this._targetApp.get_busy());
if (isBusy)
this.startAnimation();
else
this.stopAnimation();
this.reactive = (visible && !isBusy);
this.reactive = visible && !isBusy;
this._syncIcon();
this.menu.setApp(this._targetApp);
@ -430,16 +437,13 @@ class ActivitiesButton extends PanelMenu.Button {
this.label_actor = this._label;
this.connect('captured-event', this._onCapturedEvent.bind(this));
this.connect_after('key-release-event', this._onKeyRelease.bind(this));
Main.overview.connect('showing', () => {
this.add_style_pseudo_class('overview');
this.add_accessible_state (Atk.StateType.CHECKED);
this.add_accessible_state(Atk.StateType.CHECKED);
});
Main.overview.connect('hiding', () => {
this.remove_style_pseudo_class('overview');
this.remove_accessible_state (Atk.StateType.CHECKED);
this.remove_accessible_state(Atk.StateType.CHECKED);
});
this._xdndTimeOut = 0;
@ -459,7 +463,7 @@ class ActivitiesButton extends PanelMenu.Button {
return DND.DragMotionResult.CONTINUE;
}
_onCapturedEvent(actor, event) {
vfunc_captured_event(event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS ||
event.type() == Clutter.EventType.TOUCH_BEGIN) {
if (!Main.overview.shouldToggleByCornerOrButton())
@ -468,23 +472,25 @@ class ActivitiesButton extends PanelMenu.Button {
return Clutter.EVENT_PROPAGATE;
}
_onEvent(actor, event) {
super._onEvent(actor, event);
vfunc_event(event) {
if (event.type() == Clutter.EventType.TOUCH_END ||
event.type() == Clutter.EventType.BUTTON_RELEASE)
event.type() == Clutter.EventType.BUTTON_RELEASE) {
if (Main.overview.shouldToggleByCornerOrButton())
Main.overview.toggle();
}
return Clutter.EVENT_PROPAGATE;
}
_onKeyRelease(actor, event) {
let symbol = event.get_key_symbol();
vfunc_key_release_event(keyEvent) {
let symbol = keyEvent.keyval;
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
if (Main.overview.shouldToggleByCornerOrButton())
if (Main.overview.shouldToggleByCornerOrButton()) {
Main.overview.toggle();
return Clutter.EVENT_STOP;
}
}
return Clutter.EVENT_PROPAGATE;
}
@ -501,13 +507,12 @@ class ActivitiesButton extends PanelMenu.Button {
}
});
var PanelCorner = class {
constructor(side) {
var PanelCorner = GObject.registerClass(
class PanelCorner extends St.DrawingArea {
_init(side) {
this._side = side;
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', this._styleChanged.bind(this));
this.actor.connect('repaint', this._repaint.bind(this));
super._init({ style_class: 'panel-corner' });
}
_findRightmostButton(container) {
@ -528,8 +533,8 @@ var PanelCorner = class {
if (index < 0)
return null;
if (!(children[index].has_style_class_name('panel-menu')) &&
!(children[index].has_style_class_name('panel-button')))
if (!children[index].has_style_class_name('panel-menu') &&
!children[index].has_style_class_name('panel-button'))
return this._findRightmostButton(children[index]);
return children[index];
@ -553,8 +558,8 @@ var PanelCorner = class {
if (index == children.length)
return null;
if (!(children[index].has_style_class_name('panel-menu')) &&
!(children[index].has_style_class_name('panel-button')))
if (!children[index].has_style_class_name('panel-menu') &&
!children[index].has_style_class_name('panel-button'))
return this._findLeftmostButton(children[index]);
return children[index];
@ -597,7 +602,7 @@ var PanelCorner = class {
this._buttonStyleChangedSignalId = button.connect('style-changed',
() => {
let pseudoClass = button.get_style_pseudo_class();
this.actor.set_style_pseudo_class(pseudoClass);
this.set_style_pseudo_class(pseudoClass);
});
// The corner doesn't support theme transitions, so override
@ -606,8 +611,8 @@ var PanelCorner = class {
}
}
_repaint() {
let node = this.actor.get_theme_node();
vfunc_repaint() {
let node = this.get_theme_node();
let cornerRadius = node.get_length("-panel-corner-radius");
let borderWidth = node.get_length('-panel-corner-border-width');
@ -618,18 +623,19 @@ var PanelCorner = class {
let overlap = borderColor.alpha != 0;
let offsetY = overlap ? 0 : borderWidth;
let cr = this.actor.get_context();
let cr = this.get_context();
cr.setOperator(Cairo.Operator.SOURCE);
cr.moveTo(0, offsetY);
if (this._side == St.Side.LEFT)
if (this._side == St.Side.LEFT) {
cr.arc(cornerRadius,
borderWidth + cornerRadius,
cornerRadius, Math.PI, 3 * Math.PI / 2);
else
} else {
cr.arc(0,
borderWidth + cornerRadius,
cornerRadius, 3 * Math.PI / 2, 2 * Math.PI);
}
cr.lineTo(cornerRadius, offsetY);
cr.closePath();
@ -645,7 +651,7 @@ var PanelCorner = class {
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.translate(xOffsetDirection * offset, -offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
@ -654,16 +660,17 @@ var PanelCorner = class {
cr.$dispose();
}
_styleChanged() {
let node = this.actor.get_theme_node();
vfunc_style_changed() {
super.vfunc_style_changed();
let node = this.get_theme_node();
let cornerRadius = node.get_length("-panel-corner-radius");
let borderWidth = node.get_length('-panel-corner-border-width');
this.actor.set_size(cornerRadius, borderWidth + cornerRadius);
this.actor.set_anchor_point(0, borderWidth);
this.set_size(cornerRadius, borderWidth + cornerRadius);
this.set_anchor_point(0, borderWidth);
}
};
});
var AggregateLayout = GObject.registerClass(
class AggregateLayout extends Clutter.BoxLayout {
@ -706,16 +713,15 @@ class AggregateMenu extends PanelMenu.Button {
this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
this.add_child(this._indicators);
if (Config.HAVE_NETWORKMANAGER) {
if (Config.HAVE_NETWORKMANAGER)
this._network = new imports.ui.status.network.NMApplet();
} else {
else
this._network = null;
}
if (Config.HAVE_BLUETOOTH) {
if (Config.HAVE_BLUETOOTH)
this._bluetooth = new imports.ui.status.bluetooth.Indicator();
} else {
else
this._bluetooth = null;
}
this._remoteAccess = new imports.ui.status.remoteAccess.RemoteAccessApplet();
this._power = new imports.ui.status.power.Indicator();
@ -728,42 +734,41 @@ class AggregateMenu extends PanelMenu.Button {
this._nightLight = new imports.ui.status.nightLight.Indicator();
this._thunderbolt = new imports.ui.status.thunderbolt.Indicator();
this._indicators.add_child(this._thunderbolt.indicators);
this._indicators.add_child(this._screencast.indicators);
this._indicators.add_child(this._location.indicators);
this._indicators.add_child(this._nightLight.indicators);
if (this._network) {
this._indicators.add_child(this._network.indicators);
}
if (this._bluetooth) {
this._indicators.add_child(this._bluetooth.indicators);
}
this._indicators.add_child(this._remoteAccess.indicators);
this._indicators.add_child(this._rfkill.indicators);
this._indicators.add_child(this._volume.indicators);
this._indicators.add_child(this._power.indicators);
this._indicators.add_child(this._thunderbolt);
this._indicators.add_child(this._screencast);
this._indicators.add_child(this._location);
this._indicators.add_child(this._nightLight);
if (this._network)
this._indicators.add_child(this._network);
if (this._bluetooth)
this._indicators.add_child(this._bluetooth);
this._indicators.add_child(this._remoteAccess);
this._indicators.add_child(this._rfkill);
this._indicators.add_child(this._volume);
this._indicators.add_child(this._power);
this._indicators.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.menu.addMenuItem(this._volume.menu);
this.menu.addMenuItem(this._brightness.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
if (this._network) {
if (this._network)
this.menu.addMenuItem(this._network.menu);
}
if (this._bluetooth) {
if (this._bluetooth)
this.menu.addMenuItem(this._bluetooth.menu);
}
this.menu.addMenuItem(this._remoteAccess.menu);
this.menu.addMenuItem(this._location.menu);
this.menu.addMenuItem(this._rfkill.menu);
this.menu.addMenuItem(this._power.menu);
this.menu.addMenuItem(this._nightLight.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this._system.menu);
menuLayout.addSizeChild(this._location.menu.actor);
menuLayout.addSizeChild(this._rfkill.menu.actor);
menuLayout.addSizeChild(this._power.menu.actor);
menuLayout.addSizeChild(this._system.buttonGroup);
menuLayout.addSizeChild(this._system.menu.actor);
}
});
@ -799,14 +804,10 @@ class Panel extends St.Widget {
this.add_child(this._rightBox);
this._leftCorner = new PanelCorner(St.Side.LEFT);
this.add_child(this._leftCorner.actor);
this.add_child(this._leftCorner);
this._rightCorner = new PanelCorner(St.Side.RIGHT);
this.add_child(this._rightCorner.actor);
this.connect('button-press-event', this._onButtonPress.bind(this));
this.connect('touch-event', this._onButtonPress.bind(this));
this.connect('key-press-event', this._onKeyPress.bind(this));
this.add_child(this._rightCorner);
Main.overview.connect('showing', () => {
this.add_style_pseudo_class('overview');
@ -895,62 +896,65 @@ class Panel extends St.Widget {
let cornerWidth, cornerHeight;
[, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
[, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
[, cornerWidth] = this._leftCorner.get_preferred_width(-1);
[, cornerHeight] = this._leftCorner.get_preferred_height(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.actor.allocate(childBox, flags);
this._leftCorner.allocate(childBox, flags);
[, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
[, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
[, cornerWidth] = this._rightCorner.get_preferred_width(-1);
[, cornerHeight] = this._rightCorner.get_preferred_height(-1);
childBox.x1 = allocWidth - cornerWidth;
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._rightCorner.actor.allocate(childBox, flags);
this._rightCorner.allocate(childBox, flags);
}
_onButtonPress(actor, event) {
_tryDragWindow(event) {
if (Main.modalCount > 0)
return Clutter.EVENT_PROPAGATE;
if (event.get_source() != actor)
if (event.source != this)
return Clutter.EVENT_PROPAGATE;
let type = event.type();
let isPress = type == Clutter.EventType.BUTTON_PRESS;
if (!isPress && type != Clutter.EventType.TOUCH_BEGIN)
return Clutter.EVENT_PROPAGATE;
let button = isPress ? event.get_button() : -1;
if (isPress && button != 1)
return Clutter.EVENT_PROPAGATE;
let [stageX, stageY] = event.get_coords();
let dragWindow = this._getDraggableWindowForPosition(stageX);
let { x, y } = event;
let dragWindow = this._getDraggableWindowForPosition(x);
if (!dragWindow)
return Clutter.EVENT_PROPAGATE;
global.display.begin_grab_op(dragWindow,
Meta.GrabOp.MOVING,
false, /* pointer grab */
true, /* frame action */
button,
event.get_state(),
event.get_time(),
stageX, stageY);
return Clutter.EVENT_STOP;
return global.display.begin_grab_op(
dragWindow,
Meta.GrabOp.MOVING,
false, /* pointer grab */
true, /* frame action */
event.button || -1,
event.modifier_state,
event.time,
x, y) ? Clutter.EVENT_STOP : Clutter.EVENT_PROPAGATE;
}
_onKeyPress(actor, event) {
let symbol = event.get_key_symbol();
vfunc_button_press_event(buttonEvent) {
if (buttonEvent.button != 1)
return Clutter.EVENT_PROPAGATE;
return this._tryDragWindow(buttonEvent);
}
vfunc_touch_event(touchEvent) {
if (touchEvent.type != Clutter.EventType.TOUCH_BEGIN)
return Clutter.EVENT_PROPAGATE;
return this._tryDragWindow(touchEvent);
}
vfunc_key_press_event(keyEvent) {
let symbol = keyEvent.keyval;
if (symbol == Clutter.KEY_Escape) {
global.display.focus_default_window(event.get_time());
global.display.focus_default_window(keyEvent.time);
return Clutter.EVENT_STOP;
}
@ -1104,7 +1108,7 @@ class Panel extends St.Widget {
let boxes = {
left: this._leftBox,
center: this._centerBox,
right: this._rightBox
right: this._rightBox,
};
let boxContainer = boxes[box] || this._rightBox;
this.statusArea[role] = indicator;
@ -1114,14 +1118,14 @@ class Panel extends St.Widget {
_addStyleClassName(className) {
this.add_style_class_name(className);
this._rightCorner.actor.add_style_class_name(className);
this._leftCorner.actor.add_style_class_name(className);
this._rightCorner.add_style_class_name(className);
this._leftCorner.add_style_class_name(className);
}
_removeStyleClassName(className) {
this.remove_style_class_name(className);
this._rightCorner.actor.remove_style_class_name(className);
this._leftCorner.actor.remove_style_class_name(className);
this._rightCorner.remove_style_class_name(className);
this._leftCorner.remove_style_class_name(className);
}
_onMenuSet(indicator) {

View File

@ -2,7 +2,6 @@
/* exported Button, SystemIndicator */
const { Atk, Clutter, GObject, St } = imports.gi;
const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
@ -11,15 +10,17 @@ const PopupMenu = imports.ui.popupMenu;
var ButtonBox = GObject.registerClass(
class ButtonBox extends St.Widget {
_init(params) {
params = Params.parse(params, { style_class: 'panel-button' }, true);
params = Params.parse(params, {
style_class: 'panel-button',
x_expand: true,
y_expand: true,
}, true);
super._init(params);
this._delegate = this;
this.container = new St.Bin({ y_fill: true,
x_fill: true,
child: this });
this.container = new St.Bin({ child: this });
this.connect('style-changed', this._onStyleChanged.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
@ -101,9 +102,6 @@ var Button = GObject.registerClass({
accessible_name: nameText ? nameText : "",
accessible_role: Atk.Role.MENU });
this.connect('event', this._onEvent.bind(this));
this.connect('notify::visible', this._onVisibilityChanged.bind(this));
if (dontCreateMenu)
this.menu = new PopupMenu.PopupDummyMenu(this);
else
@ -132,7 +130,7 @@ var Button = GObject.registerClass({
this.emit('menu-set');
}
_onEvent(actor, event) {
vfunc_event(event) {
if (this.menu &&
(event.type() == Clutter.EventType.TOUCH_BEGIN ||
event.type() == Clutter.EventType.BUTTON_PRESS))
@ -141,11 +139,10 @@ var Button = GObject.registerClass({
return Clutter.EVENT_PROPAGATE;
}
_onVisibilityChanged() {
if (!this.menu)
return;
vfunc_hide() {
super.vfunc_hide();
if (!this.visible)
if (this.menu)
this.menu.close();
}
@ -157,7 +154,7 @@ var Button = GObject.registerClass({
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let group = global.focus_manager.get_group(this);
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? St.DirectionType.LEFT : St.DirectionType.RIGHT;
let direction = symbol == Clutter.KEY_Left ? St.DirectionType.LEFT : St.DirectionType.RIGHT;
group.navigate_focus(this, direction, false);
return Clutter.EVENT_STOP;
}
@ -182,7 +179,7 @@ var Button = GObject.registerClass({
// measures are in logical pixels, so make sure to consider the scale
// factor when computing max-height
let maxHeight = Math.round((workArea.height - verticalMargins) / scaleFactor);
this.menu.actor.style = ('max-height: %spx;').format(maxHeight);
this.menu.actor.style = 'max-height: %spx;'.format(maxHeight);
}
_onDestroy() {
@ -200,24 +197,33 @@ var Button = GObject.registerClass({
* of an icon and a menu section, which will be composed into the
* aggregate menu.
*/
var SystemIndicator = class {
constructor() {
this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box',
reactive: true });
this.indicators.hide();
var SystemIndicator = GObject.registerClass(
class SystemIndicator extends St.BoxLayout {
_init() {
super._init({
style_class: 'panel-status-indicators-box',
reactive: true,
visible: false,
});
this.menu = new PopupMenu.PopupMenuSection();
}
get indicators() {
let klass = this.constructor.name;
let { stack } = new Error();
log(`Usage of indicator.indicators is deprecated for ${klass}\n${stack}`);
return this;
}
_syncIndicatorsVisible() {
this.indicators.visible = this.indicators.get_children().some(a => a.visible);
this.visible = this.get_children().some(a => a.visible);
}
_addIndicator() {
let icon = new St.Icon({ style_class: 'system-status-icon' });
this.indicators.add_actor(icon);
this.add_actor(icon);
icon.connect('notify::visible', this._syncIndicatorsVisible.bind(this));
this._syncIndicatorsVisible();
return icon;
}
};
Signals.addSignalMethods(SystemIndicator.prototype);
});

View File

@ -10,8 +10,8 @@ var PieTimer = GObject.registerClass({
'angle': GObject.ParamSpec.double(
'angle', 'angle', 'angle',
GObject.ParamFlags.READWRITE,
0, 2 * Math.PI, 0)
}
0, 2 * Math.PI, 0),
},
}, class PieTimer extends St.DrawingArea {
_init() {
this._angle = 0;
@ -20,7 +20,7 @@ var PieTimer = GObject.registerClass({
opacity: 0,
visible: false,
can_focus: false,
reactive: false
reactive: false,
});
this.set_pivot_point(0.5, 0.5);
@ -84,13 +84,13 @@ var PieTimer = GObject.registerClass({
this.ease({
opacity: 255,
duration: duration / 4,
mode: Clutter.AnimationMode.EASE_IN_QUAD
mode: Clutter.AnimationMode.EASE_IN_QUAD,
});
this.ease_property('angle', 2 * Math.PI, {
duration,
mode: Clutter.AnimationMode.LINEAR,
onComplete: this._onTransitionComplete.bind(this)
onComplete: this._onTransitionComplete.bind(this),
});
}
@ -101,7 +101,7 @@ var PieTimer = GObject.registerClass({
opacity: 0,
duration: SUCCESS_ZOOM_OUT_DURATION,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onStopped: () => this.destroy()
onStopped: () => this.destroy(),
});
}
});
@ -110,7 +110,7 @@ var PointerA11yTimeout = class PointerA11yTimeout {
constructor() {
let manager = Clutter.DeviceManager.get_default();
manager.connect('ptr-a11y-timeout-started', (manager, device, type, timeout) => {
manager.connect('ptr-a11y-timeout-started', (o, device, type, timeout) => {
let [x, y] = global.get_pointer();
this._pieTimer = new PieTimer();
@ -123,7 +123,7 @@ var PointerA11yTimeout = class PointerA11yTimeout {
global.display.set_cursor(Meta.Cursor.CROSSHAIR);
});
manager.connect('ptr-a11y-timeout-stopped', (manager, device, type, clicked) => {
manager.connect('ptr-a11y-timeout-stopped', (o, device, type, clicked) => {
if (!clicked)
this._pieTimer.destroy();

View File

@ -3,7 +3,7 @@
PopupImageMenuItem, PopupMenu, PopupDummyMenu, PopupSubMenu,
PopupMenuSection, PopupSubMenuMenuItem, PopupMenuManager */
const { Atk, Clutter, Gio, GObject, Shell, St } = imports.gi;
const { Atk, Clutter, Gio, GObject, Graphene, Shell, St } = imports.gi;
const Signals = imports.signals;
const BoxPointer = imports.ui.boxpointer;
@ -19,14 +19,17 @@ var Ornament = {
};
function isPopupMenuItemVisible(child) {
if (child._delegate instanceof PopupMenuSection)
if (child._delegate instanceof PopupMenuSection) {
if (child._delegate.isEmpty())
return false;
}
return child.visible;
}
/**
* @side Side to which the arrow points.
* arrowIcon
* @param {St.Side} side - Side to which the arrow points.
* @returns {St.Icon} a new arrow icon
*/
function arrowIcon(side) {
let iconName;
@ -65,10 +68,10 @@ var PopupBaseMenuItem = GObject.registerClass({
},
Signals: {
'activate': { param_types: [Clutter.Event.$gtype] },
}
},
}, class PopupBaseMenuItem extends St.BoxLayout {
_init(params) {
params = Params.parse (params, {
params = Params.parse(params, {
reactive: true,
activate: true,
hover: true,
@ -97,12 +100,6 @@ var PopupBaseMenuItem = GObject.registerClass({
if (params.style_class)
this.add_style_class_name(params.style_class);
if (this._activatable) {
this.connect('button-press-event', this._onButtonPressEvent.bind(this));
this.connect('button-release-event', this._onButtonReleaseEvent.bind(this));
this.connect('touch-event', this._onTouchEvent.bind(this));
this.connect('key-press-event', this._onKeyPressEvent.bind(this));
}
if (params.reactive && params.hover)
this.bind_property('hover', this, 'active', GObject.BindingFlags.SYNC_CREATE);
}
@ -124,32 +121,44 @@ var PopupBaseMenuItem = GObject.registerClass({
this._parent = parent;
}
_onButtonPressEvent() {
vfunc_button_press_event() {
if (!this._activatable)
return Clutter.EVENT_PROPAGATE;
// This is the CSS active state
this.add_style_pseudo_class('active');
return Clutter.EVENT_PROPAGATE;
}
_onButtonReleaseEvent(actor, event) {
vfunc_button_release_event() {
if (!this._activatable)
return Clutter.EVENT_PROPAGATE;
this.remove_style_pseudo_class('active');
this.activate(event);
this.activate(Clutter.get_current_event());
return Clutter.EVENT_STOP;
}
_onTouchEvent(actor, event) {
if (event.type() == Clutter.EventType.TOUCH_END) {
vfunc_touch_event(touchEvent) {
if (!this._activatable)
return Clutter.EVENT_PROPAGATE;
if (touchEvent.type == Clutter.EventType.TOUCH_END) {
this.remove_style_pseudo_class('active');
this.activate(event);
this.activate(Clutter.get_current_event());
return Clutter.EVENT_STOP;
} else if (event.type() == Clutter.EventType.TOUCH_BEGIN) {
} else if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN) {
// This is the CSS active state
this.add_style_pseudo_class('active');
}
return Clutter.EVENT_PROPAGATE;
}
_onKeyPressEvent(actor, event) {
let state = event.get_state();
vfunc_key_press_event(keyEvent) {
if (!this._activatable)
return super.vfunc_key_press_event(keyEvent);
let state = keyEvent.modifier_state;
// if user has a modifier down (except capslock and numlock)
// then don't handle the key press here
@ -160,9 +169,9 @@ var PopupBaseMenuItem = GObject.registerClass({
if (state)
return Clutter.EVENT_PROPAGATE;
let symbol = event.get_key_symbol();
let symbol = keyEvent.keyval;
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.activate(event);
this.activate(Clutter.get_current_event());
return Clutter.EVENT_STOP;
}
return Clutter.EVENT_PROPAGATE;
@ -263,7 +272,7 @@ class PopupMenuItem extends PopupBaseMenuItem {
_init(text, params) {
super._init(params);
this.label = new St.Label({ text: text });
this.label = new St.Label({ text });
this.add_child(this.label);
this.label_actor = this.label;
}
@ -285,9 +294,10 @@ class PopupSeparatorMenuItem extends PopupBaseMenuItem {
this._syncVisibility();
this._separator = new St.Widget({ style_class: 'popup-separator-menu-item',
x_expand: true,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
this.add(this._separator, { expand: true });
this.add_child(this._separator);
}
_syncVisibility() {
@ -318,12 +328,12 @@ class Switch extends St.Bin {
});
var PopupSwitchMenuItem = GObject.registerClass({
Signals: { 'toggled': { param_types: [GObject.TYPE_BOOLEAN] }, },
Signals: { 'toggled': { param_types: [GObject.TYPE_BOOLEAN] } },
}, class PopupSwitchMenuItem extends PopupBaseMenuItem {
_init(text, active, params) {
super._init(params);
this.label = new St.Label({ text: text });
this.label = new St.Label({ text });
this._switch = new Switch(active);
this.accessible_role = Atk.Role.CHECK_MENU_ITEM;
@ -332,8 +342,11 @@ var PopupSwitchMenuItem = GObject.registerClass({
this.add_child(this.label);
this._statusBin = new St.Bin({ x_align: St.Align.END });
this.add(this._statusBin, { expand: true, x_align: St.Align.END });
this._statusBin = new St.Bin({
x_align: Clutter.ActorAlign.END,
x_expand: true,
});
this.add_child(this._statusBin);
this._statusLabel = new St.Label({
text: '',
@ -406,7 +419,7 @@ class PopupImageMenuItem extends PopupBaseMenuItem {
this._icon = new St.Icon({ style_class: 'popup-menu-icon',
x_align: Clutter.ActorAlign.END });
this.add_child(this._icon);
this.label = new St.Label({ text: text });
this.label = new St.Label({ text });
this.add_child(this.label);
this.label_actor = this.label;
@ -431,12 +444,14 @@ var PopupMenuBase = class {
this.focusActor = sourceActor;
this._parent = null;
if (styleClass !== undefined) {
this.box = new St.BoxLayout({ style_class: styleClass,
vertical: true });
} else {
this.box = new St.BoxLayout({ vertical: true });
}
this.box = new St.BoxLayout({
vertical: true,
x_expand: true,
y_expand: true,
});
if (styleClass !== undefined)
this.box.style_class = styleClass;
this.length = 0;
this.isOpen = false;
@ -495,7 +510,7 @@ var PopupMenuBase = class {
menuItem = new PopupMenuItem(title);
this.addMenuItem(menuItem);
menuItem.connect('activate', (menuItem, event) => {
menuItem.connect('activate', (o, event) => {
callback(event);
});
@ -553,7 +568,7 @@ var PopupMenuBase = class {
}
_connectItemSignals(menuItem) {
menuItem._activeChangeId = menuItem.connect('notify::active', menuItem => {
menuItem._activeChangeId = menuItem.connect('notify::active', () => {
let active = menuItem.active;
if (active && this._activeMenuItem != menuItem) {
if (this._activeMenuItem)
@ -577,7 +592,7 @@ var PopupMenuBase = class {
menuItem.actor.grab_key_focus();
}
});
menuItem._activateId = menuItem.connect_after('activate', (menuItem, _event) => {
menuItem._activateId = menuItem.connect_after('activate', () => {
this.emit('activate', menuItem);
this.itemActivated(BoxPointer.PopupAnimation.FULL);
});
@ -590,7 +605,7 @@ var PopupMenuBase = class {
// the menuItem may have, called destroyId
// (FIXME: in the future it may make sense to have container objects
// like PopupMenuManager does)
menuItem._popupMenuDestroyId = menuItem.connect('destroy', menuItem => {
menuItem._popupMenuDestroyId = menuItem.connect('destroy', () => {
menuItem.disconnect(menuItem._popupMenuDestroyId);
menuItem.disconnect(menuItem._activateId);
menuItem.disconnect(menuItem._activeChangeId);
@ -786,10 +801,7 @@ var PopupMenu = class extends PopupMenuBase {
this._arrowAlignment = arrowAlignment;
this._arrowSide = arrowSide;
this._boxPointer = new BoxPointer.BoxPointer(arrowSide,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._boxPointer = new BoxPointer.BoxPointer(arrowSide);
this.actor = this._boxPointer;
this.actor._delegate = this;
this.actor.style_class = 'popup-menu-boxpointer';
@ -800,10 +812,12 @@ var PopupMenu = class extends PopupMenuBase {
global.focus_manager.add_group(this.actor);
this.actor.reactive = true;
if (this.sourceActor)
if (this.sourceActor) {
this._keyPressId = this.sourceActor.connect('key-press-event',
this._onKeyPress.bind(this));
}
this._systemModalOpenedId = 0;
this._openedSubMenu = null;
}
@ -878,12 +892,17 @@ var PopupMenu = class extends PopupMenuBase {
if (this.isEmpty())
return;
if (!this._systemModalOpenedId) {
this._systemModalOpenedId =
Main.layoutManager.connect('system-modal-opened', () => this.close());
}
this.isOpen = true;
this._boxPointer.setPosition(this.sourceActor, this._arrowAlignment);
this._boxPointer.open(animate);
this.actor.raise_top();
this.actor.get_parent().set_child_above_sibling(this.actor, null);
this.emit('open-state-changed', true);
}
@ -908,6 +927,11 @@ var PopupMenu = class extends PopupMenuBase {
destroy() {
if (this._keyPressId)
this.sourceActor.disconnect(this._keyPressId);
if (this._systemModalOpenedId)
Main.layoutManager.disconnect(this._systemModalOpenedId);
this._systemModalOpenedId = 0;
super.destroy();
}
};
@ -1021,12 +1045,12 @@ var PopupSubMenu = class extends PopupMenuBase {
height: naturalHeight,
duration: 250,
mode: Clutter.AnimationMode.EASE_OUT_EXPO,
onComplete: () => this.actor.set_height(-1)
onComplete: () => this.actor.set_height(-1),
});
this._arrow.ease({
rotation_angle_z: targetAngle,
duration: 250,
mode: Clutter.AnimationMode.EASE_OUT_EXPO
mode: Clutter.AnimationMode.EASE_OUT_EXPO,
});
} else {
this._arrow.rotation_angle_z = targetAngle;
@ -1054,12 +1078,12 @@ var PopupSubMenu = class extends PopupMenuBase {
onComplete: () => {
this.actor.hide();
this.actor.set_height(-1);
}
},
});
this._arrow.ease({
rotation_angle_z: 0,
duration: 250,
mode: Clutter.AnimationMode.EASE_OUT_EXPO
mode: Clutter.AnimationMode.EASE_OUT_EXPO,
});
} else {
this._arrow.rotation_angle_z = 0;
@ -1120,17 +1144,20 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem {
this.add_child(this.icon);
}
this.label = new St.Label({ text: text,
this.label = new St.Label({ text,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
this.add_child(this.label);
this.label_actor = this.label;
let expander = new St.Bin({ style_class: 'popup-menu-item-expander' });
this.add(expander, { expand: true });
let expander = new St.Bin({
style_class: 'popup-menu-item-expander',
x_expand: true,
});
this.add_child(expander);
this._triangle = arrowIcon(St.Side.RIGHT);
this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
this._triangle.pivot_point = new Graphene.Point({ x: 0.5, y: 0.6 });
this._triangleBin = new St.Widget({ y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
@ -1165,7 +1192,7 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem {
} else {
this.remove_style_pseudo_class('open');
this._getTopMenu()._setOpenedSubMenu(null);
this.remove_accessible_state (Atk.StateType.EXPANDED);
this.remove_accessible_state(Atk.StateType.EXPANDED);
this.remove_style_pseudo_class('checked');
}
}
@ -1185,8 +1212,8 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem {
return this.menu.isOpen;
}
_onKeyPressEvent(actor, event) {
let symbol = event.get_key_symbol();
vfunc_key_press_event(keyPressEvent) {
let symbol = keyPressEvent.keyval;
if (symbol == Clutter.KEY_Right) {
this._setOpenState(true);
@ -1197,14 +1224,14 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem {
return Clutter.EVENT_STOP;
}
return super._onKeyPressEvent(actor, event);
return super.vfunc_key_press_event(keyPressEvent);
}
activate(_event) {
this._setOpenState(true);
}
_onButtonReleaseEvent() {
vfunc_button_release_event() {
// Since we override the parent, we need to manage what the parent does
// with the active style class
this.remove_style_pseudo_class('active');
@ -1212,8 +1239,8 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem {
return Clutter.EVENT_PROPAGATE;
}
_onTouchEvent(actor, event) {
if (event.type() == Clutter.EventType.TOUCH_END) {
vfunc_touch_event(touchEvent) {
if (touchEvent.type == Clutter.EventType.TOUCH_END) {
// Since we override the parent, we need to manage what the parent does
// with the active style class
this.remove_style_pseudo_class('active');
@ -1239,11 +1266,11 @@ var PopupMenuManager = class {
return;
let menudata = {
menu: menu,
menu,
openStateChangeId: menu.connect('open-state-changed', this._onMenuOpenState.bind(this)),
destroyId: menu.connect('destroy', this._onMenuDestroy.bind(this)),
enterId: 0,
focusInId: 0
focusInId: 0,
};
let source = menu.sourceActor;

View File

@ -181,7 +181,7 @@ function loadRemoteSearchProviders(searchSettings, callback) {
return -1;
// finally, if both providers are found, return their order in the list
return (idxA - idxB);
return idxA - idxB;
});
callback(loadedProviders);
@ -228,8 +228,7 @@ var RemoteSearchProvider = class {
}
if (gicon)
icon = new St.Icon({ gicon: gicon,
icon_size: size });
icon = new St.Icon({ gicon, icon_size: size });
return icon;
}

View File

@ -53,13 +53,13 @@ var Ripples = class Ripples {
ripple.visible = true;
ripple.opacity = 255 * Math.sqrt(startOpacity);
ripple.scale_x = ripple.scale_y = startScale;
ripple.set_translation( - this._px * ripple.width, - this._py * ripple.height, 0.0);
ripple.set_translation(-this._px * ripple.width, -this._py * ripple.height, 0.0);
ripple.ease({
opacity: 0,
delay,
duration,
mode: Clutter.AnimationMode.EASE_IN_QUAD
mode: Clutter.AnimationMode.EASE_IN_QUAD,
});
ripple.ease({
scale_x: finalScale,
@ -67,7 +67,7 @@ var Ripples = class Ripples {
delay,
duration,
mode: Clutter.AnimationMode.LINEAR,
onComplete: () => (ripple.visible = false)
onComplete: () => (ripple.visible = false),
});
}

View File

@ -57,9 +57,7 @@ class RunDialog extends ModalDialog.ModalDialog {
let label = new St.Label({ style_class: 'run-dialog-label',
text: _("Enter a Command") });
this.contentLayout.add(label, { x_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this.contentLayout.add_child(label);
let entry = new St.Entry({ style_class: 'run-dialog-entry',
can_focus: true });
@ -68,36 +66,36 @@ class RunDialog extends ModalDialog.ModalDialog {
entry.label_actor = label;
this._entryText = entry.clutter_text;
this.contentLayout.add(entry, { y_align: St.Align.START });
this.contentLayout.add_child(entry);
this.setInitialKeyFocus(this._entryText);
this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' });
this.contentLayout.add(this._errorBox, { expand: true });
this.contentLayout.add_child(this._errorBox);
let errorIcon = new St.Icon({ icon_name: 'dialog-error-symbolic',
icon_size: 24,
style_class: 'run-dialog-error-icon' });
style_class: 'run-dialog-error-icon',
y_align: Clutter.ActorAlign.CENTER });
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
this._errorBox.add_child(errorIcon);
this._commandError = false;
this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' });
this._errorMessage = new St.Label({
style_class: 'run-dialog-error-label',
y_align: Clutter.ActorAlign.CENTER,
});
this._errorMessage.clutter_text.line_wrap = true;
this._errorBox.add(this._errorMessage, { expand: true,
x_align: St.Align.START,
x_fill: false,
y_align: St.Align.MIDDLE,
y_fill: false });
this._errorBox.add_child(this._errorMessage);
this._errorBox.hide();
this.setButtons([{
action: this.close.bind(this),
label: _("Close"),
key: Clutter.Escape,
key: Clutter.KEY_Escape,
}]);
this._pathCompleter = new Gio.FilenameCompleter();
@ -114,7 +112,7 @@ class RunDialog extends ModalDialog.ModalDialog {
});
this._entryText.connect('key-press-event', (o, e) => {
let symbol = e.get_key_symbol();
if (symbol == Clutter.Tab) {
if (symbol === Clutter.KEY_Tab) {
let text = o.get_text();
let prefix;
if (text.lastIndexOf(' ') == -1)
@ -177,11 +175,10 @@ class RunDialog extends ModalDialog.ModalDialog {
}
_getCompletion(text) {
if (text.includes('/')) {
if (text.includes('/'))
return this._pathCompleter.get_completion_suffix(text);
} else {
else
return this._getCommandCompletion(text);
}
}
_run(input, inTerminal) {
@ -212,7 +209,7 @@ class RunDialog extends ModalDialog.ModalDialog {
} else {
if (input.charAt(0) == '~')
input = input.slice(1);
path = GLib.get_home_dir() + '/' + input;
path = `${GLib.get_home_dir()}/${input}`;
}
if (GLib.file_test(path, GLib.FileTest.EXISTS)) {
@ -220,12 +217,12 @@ class RunDialog extends ModalDialog.ModalDialog {
try {
Gio.app_info_launch_default_for_uri(file.get_uri(),
global.create_app_launch_context(0, -1));
} catch (e) {
} catch (err) {
// The exception from gjs contains an error string like:
// Error invoking Gio.app_info_launch_default_for_uri: No application
// is registered as handling this file
// We are only interested in the part after the first colon.
let message = e.message.replace(/[^:]*: *(.+)/, '$1');
let message = err.message.replace(/[^:]*: *(.+)/, '$1');
this._showError(message);
}
} else {
@ -252,7 +249,7 @@ class RunDialog extends ModalDialog.ModalDialog {
onComplete: () => {
parentActor.set_height(-1);
this._errorBox.show();
}
},
});
}
}

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const { AccountsService, Clutter, Cogl, Gio, GLib,
GnomeDesktop, GObject, Meta, Shell, St } = imports.gi;
const { AccountsService, Clutter, Gio, GLib,
GnomeDesktop, GObject, Graphene, Meta, Shell, St } = imports.gi;
const Cairo = imports.cairo;
const Signals = imports.signals;
@ -17,6 +17,8 @@ const MessageTray = imports.ui.messageTray;
const ShellDBus = imports.ui.shellDBus;
const SmartcardManager = imports.misc.smartcardManager;
const { adjustAnimationTime } = imports.ui.environment;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
const LOCK_DELAY_KEY = 'lock-delay';
@ -46,21 +48,29 @@ var STANDARD_FADE_TIME = 10000;
var MANUAL_FADE_TIME = 300;
var CURTAIN_SLIDE_TIME = 300;
var Clock = class {
constructor() {
this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
vertical: true });
var Clock = GObject.registerClass(
class ScreenShieldClock extends St.BoxLayout {
_init() {
super._init({ style_class: 'screen-shield-clock', vertical: true });
this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
this._time = new St.Label({
style_class: 'screen-shield-clock-time',
x_align: Clutter.ActorAlign.CENTER,
});
this._date = new St.Label({
style_class: 'screen-shield-clock-date',
x_align: Clutter.ActorAlign.CENTER,
});
this.actor.add(this._time, { x_align: St.Align.MIDDLE });
this.actor.add(this._date, { x_align: St.Align.MIDDLE });
this.add_child(this._time);
this.add_child(this._date);
this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
this._wallClock.connect('notify::clock', this._updateClock.bind(this));
this._updateClock();
this.connect('destroy', this._onDestroy.bind(this));
}
_updateClock() {
@ -73,25 +83,27 @@ var Clock = class {
this._date.text = date.toLocaleFormat(dateFormat);
}
destroy() {
this.actor.destroy();
_onDestroy() {
this._wallClock.run_dispose();
}
};
});
var NotificationsBox = class {
constructor() {
this.actor = new St.BoxLayout({ vertical: true,
name: 'screenShieldNotifications',
style_class: 'screen-shield-notifications-container' });
var NotificationsBox = GObject.registerClass({
Signals: { 'wake-up-screen': {} },
}, class NotificationsBox extends St.BoxLayout {
_init() {
super._init({
vertical: true,
name: 'screenShieldNotifications',
style_class: 'screen-shield-notifications-container',
});
this._scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START,
hscrollbar_policy: St.PolicyType.NEVER });
this._scrollView = new St.ScrollView({ hscrollbar_policy: St.PolicyType.NEVER });
this._notificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-container' });
this._scrollView.add_actor(this._notificationBox);
this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START });
this.add_child(this._scrollView);
this._sources = new Map();
Main.messageTray.getSources().forEach(source => {
@ -100,27 +112,26 @@ var NotificationsBox = class {
this._updateVisibility();
this._sourceAddedId = Main.messageTray.connect('source-added', this._sourceAdded.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
}
destroy() {
_onDestroy() {
if (this._sourceAddedId) {
Main.messageTray.disconnect(this._sourceAddedId);
this._sourceAddedId = 0;
}
let items = this._sources.entries();
for (let [source, obj] of items) {
for (let [source, obj] of items)
this._removeSource(source, obj);
}
this.actor.destroy();
}
_updateVisibility() {
this._notificationBox.visible =
this._notificationBox.get_children().some(a => a.visible);
this.actor.visible = this._notificationBox.visible;
this.visible = this._notificationBox.visible;
}
_makeNotificationCountText(count, isChat) {
@ -132,10 +143,10 @@ var NotificationsBox = class {
_makeNotificationSource(source, box) {
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
box.add(sourceActor, { y_fill: true });
box.add_child(sourceActor);
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox, { y_fill: false, y_align: St.Align.START });
box.add_child(textBox);
let title = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
@ -152,13 +163,11 @@ var NotificationsBox = class {
_makeNotificationDetailedSource(source, box) {
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
let sourceBin = new St.Bin({ y_align: St.Align.START,
x_align: St.Align.START,
child: sourceActor });
let sourceBin = new St.Bin({ child: sourceActor });
box.add(sourceBin);
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox, { y_fill: false, y_align: St.Align.START });
box.add_child(textBox);
let title = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
@ -179,7 +188,7 @@ var NotificationsBox = class {
}
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
label.clutter_text.set_markup('<b>' + n.title + '</b> ' + body);
label.clutter_text.set_markup(`<b>${n.title}</b> ${body}`);
textBox.add(label);
visible = true;
@ -195,11 +204,10 @@ var NotificationsBox = class {
}
_showSource(source, obj, box) {
if (obj.detailed) {
if (obj.detailed)
[obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box);
} else {
else
[obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box);
}
box.visible = obj.visible && (source.unseenCount > 0);
}
@ -220,21 +228,21 @@ var NotificationsBox = class {
obj.sourceBox = new St.BoxLayout({ style_class: 'screen-shield-notification-source',
x_expand: true });
this._showSource(source, obj, obj.sourceBox);
this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
this._notificationBox.add_child(obj.sourceBox);
obj.sourceCountChangedId = source.connect('count-updated', source => {
obj.sourceCountChangedId = source.connect('notify::count', () => {
this._countChanged(source, obj);
});
obj.sourceTitleChangedId = source.connect('title-changed', source => {
obj.sourceTitleChangedId = source.connect('notify::title', () => {
this._titleChanged(source, obj);
});
obj.policyChangedId = source.policy.connect('policy-changed', (policy, key) => {
if (key == 'show-in-lock-screen')
obj.policyChangedId = source.policy.connect('notify', (policy, pspec) => {
if (pspec.name == 'show-in-lock-screen')
this._visibleChanged(source, obj);
else
this._detailedChanged(source, obj);
});
obj.sourceDestroyId = source.connect('destroy', source => {
obj.sourceDestroyId = source.connect('destroy', () => {
this._onSourceDestroy(source, obj);
});
@ -256,7 +264,7 @@ var NotificationsBox = class {
onComplete: () => {
this._scrollView.vscrollbar_policy = St.PolicyType.AUTOMATIC;
widget.set_height(-1);
}
},
});
this._updateVisibility();
@ -336,16 +344,17 @@ var NotificationsBox = class {
this._sources.delete(source);
}
};
Signals.addSignalMethods(NotificationsBox.prototype);
});
var Arrow = GObject.registerClass(
class ScreenShieldArrow extends St.Bin {
_init(params) {
super._init(params);
this.x_fill = this.y_fill = true;
this._drawingArea = new St.DrawingArea();
this._drawingArea = new St.DrawingArea({
x_expand: true,
y_expand: true,
});
this._drawingArea.connect('repaint', this._drawArrow.bind(this));
this.child = this._drawingArea;
@ -398,18 +407,18 @@ class ScreenShieldArrow extends St.Bin {
super.vfunc_style_changed();
}
vfunc_paint() {
vfunc_paint(paintContext) {
if (this._shadowHelper) {
this._shadowHelper.update(this._drawingArea);
let allocation = this._drawingArea.get_allocation_box();
let paintOpacity = this._drawingArea.get_paint_opacity();
let framebuffer = Cogl.get_draw_framebuffer();
let framebuffer = paintContext.get_framebuffer();
this._shadowHelper.paint(framebuffer, allocation, paintOpacity);
}
this._drawingArea.paint();
this._drawingArea.paint(paintContext);
}
});
@ -453,7 +462,7 @@ var ScreenShield = class {
this._backgroundGroup = new Clutter.Actor();
this._lockScreenGroup.add_actor(this._backgroundGroup);
this._backgroundGroup.lower_bottom();
this._lockScreenGroup.set_child_below_sibling(this._backgroundGroup, null);
this._bgManagers = [];
this._updateBackgrounds();
@ -486,7 +495,7 @@ var ScreenShield = class {
this._lockDialogGroup = new St.Widget({ x_expand: true,
y_expand: true,
reactive: true,
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
name: 'lockDialogGroup' });
this.actor.add_actor(this._lockDialogGroup);
@ -557,11 +566,11 @@ var ScreenShield = class {
this._longLightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
fadeFactor: 1 });
this._longLightbox.connect('shown', this._onLongLightboxShown.bind(this));
this._longLightbox.connect('notify::active', this._onLongLightbox.bind(this));
this._shortLightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
fadeFactor: 1 });
this._shortLightbox.connect('shown', this._onShortLightboxShown.bind(this));
this._shortLightbox.connect('notify::active', this._onShortLightbox.bind(this));
this.idleMonitor = Meta.IdleMonitor.get_core();
this._cursorTracker = Meta.CursorTracker.get_for_display(global.display);
@ -591,7 +600,7 @@ var ScreenShield = class {
height: monitor.height });
let bgManager = new Background.BackgroundManager({ container: widget,
monitorIndex: monitorIndex,
monitorIndex,
controlPosition: false,
settingsSchema: SCREENSAVER_SCHEMA });
@ -662,12 +671,12 @@ var ScreenShield = class {
if (this._lockScreenState != MessageTray.State.SHOWN)
return Clutter.EVENT_PROPAGATE;
let isEnter = (symbol == Clutter.KEY_Return ||
let isEnter = symbol == Clutter.KEY_Return ||
symbol == Clutter.KEY_KP_Enter ||
symbol == Clutter.KEY_ISO_Enter);
let isEscape = (symbol == Clutter.KEY_Escape);
let isLiftChar = (GLib.unichar_isprint(unichar) &&
(this._isLocked || !GLib.unichar_isgraph(unichar)));
symbol == Clutter.KEY_ISO_Enter;
let isEscape = symbol == Clutter.KEY_Escape;
let isLiftChar = GLib.unichar_isprint(unichar) &&
(this._isLocked || !GLib.unichar_isgraph(unichar));
if (!isEnter && !isEscape && !isLiftChar)
return Clutter.EVENT_PROPAGATE;
@ -693,9 +702,8 @@ var ScreenShield = class {
this._lockScreenScrollCounter += delta;
// 7 standard scrolls to lift up
if (this._lockScreenScrollCounter > 35) {
if (this._lockScreenScrollCounter > 35)
this._liftShield(true, 0);
}
return Clutter.EVENT_STOP;
}
@ -703,8 +711,8 @@ var ScreenShield = class {
_syncInhibitor() {
let lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY);
let lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY);
let inhibit = (this._loginSession && this._loginSession.Active &&
!this._isActive && lockEnabled && !lockLocked);
let inhibit = this._loginSession && this._loginSession.Active &&
!this._isActive && lockEnabled && !lockLocked;
if (inhibit) {
this._loginManager.inhibit(_("GNOME needs to lock the screen"),
inhibitor => {
@ -743,9 +751,9 @@ var ScreenShield = class {
arrows[i].ease({
opacity: 0,
duration: ARROW_ANIMATION_TIME / 2,
mode: Clutter.AnimationMode.EASE_IN_QUAD
mode: Clutter.AnimationMode.EASE_IN_QUAD,
});
}
},
});
}
@ -785,7 +793,7 @@ var ScreenShield = class {
// restore the lock screen to its original place
// try to use the same speed as the normal animation
let h = global.stage.height;
let duration = MANUAL_FADE_TIME * (-this._lockScreenGroup.y) / h;
let duration = MANUAL_FADE_TIME * -this._lockScreenGroup.y / h;
this._lockScreenGroup.remove_all_transitions();
this._lockScreenGroup.ease({
y: 0,
@ -794,7 +802,7 @@ var ScreenShield = class {
onComplete: () => {
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenState = MessageTray.State.SHOWN;
}
},
});
this._maybeCancelDialog();
@ -807,7 +815,7 @@ var ScreenShield = class {
this._maybeCancelDialog();
if (this._longLightbox.actor.visible) {
if (this._longLightbox.visible) {
// We're in the process of showing.
return;
}
@ -832,7 +840,7 @@ var ScreenShield = class {
if (shouldLock) {
let lockTimeout = Math.max(
STANDARD_FADE_TIME,
adjustAnimationTime(STANDARD_FADE_TIME),
this._settings.get_uint(LOCK_DELAY_KEY) * 1000);
this._lockTimeoutId = GLib.timeout_add(
GLib.PRIORITY_DEFAULT,
@ -849,8 +857,8 @@ var ScreenShield = class {
}
_activateFade(lightbox, time) {
Main.uiGroup.set_child_above_sibling(lightbox.actor, null);
lightbox.show(time);
Main.uiGroup.set_child_above_sibling(lightbox, null);
lightbox.lightOn(time);
if (this._becameActiveId == 0)
this._becameActiveId = this.idleMonitor.add_user_active_watch(this._onUserBecameActive.bind(this));
@ -879,19 +887,21 @@ var ScreenShield = class {
this._becameActiveId = 0;
if (this._isActive || this._isLocked) {
this._longLightbox.hide();
this._shortLightbox.hide();
this._longLightbox.lightOff();
this._shortLightbox.lightOff();
} else {
this.deactivate(false);
}
}
_onLongLightboxShown() {
this.activate(false);
_onLongLightbox(lightBox) {
if (lightBox.active)
this.activate(false);
}
_onShortLightboxShown() {
this._completeLockScreenShown();
_onShortLightbox(lightBox) {
if (lightBox.active)
this._completeLockScreenShown();
}
showDialog() {
@ -935,7 +945,7 @@ var ScreenShield = class {
// use the same speed regardless of original position
// if velocity is specified, it's in pixels per milliseconds
let h = global.stage.height;
let delta = (h + this._lockScreenGroup.y);
let delta = h + this._lockScreenGroup.y;
let minVelocity = global.stage.height / CURTAIN_SLIDE_TIME;
velocity = Math.max(minVelocity, velocity);
@ -945,7 +955,7 @@ var ScreenShield = class {
y: -h,
duration,
mode: Clutter.AnimationMode.EASE_IN_QUAD,
onComplete: () => this._hideLockScreenComplete()
onComplete: () => this._hideLockScreenComplete(),
});
} else {
this._hideLockScreenComplete();
@ -965,7 +975,6 @@ var ScreenShield = class {
this._dialog = new constructor(this._lockDialogGroup);
let time = global.get_current_time();
if (!this._dialog.open(time, onPrimary)) {
// This is kind of an impossible error: we're already modal
@ -1013,12 +1022,11 @@ var ScreenShield = class {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._lockScreenShown({ fadeToBlack, animateFade: true });
}
},
});
} else {
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenShown({ fadeToBlack: fadeToBlack,
animateFade: false });
this._lockScreenShown({ fadeToBlack, animateFade: false });
}
this._lockScreenGroup.grab_key_focus();
@ -1036,9 +1044,10 @@ var ScreenShield = class {
this._animateArrows();
}
if (!this._arrowWatchId)
if (!this._arrowWatchId) {
this._arrowWatchId = this.idleMonitor.add_idle_watch(ARROW_IDLE_TIME,
this._pauseArrowAnimation.bind(this));
}
}
_pauseArrowAnimation() {
@ -1131,16 +1140,13 @@ var ScreenShield = class {
vertical: true,
style_class: 'screen-shield-contents-box' });
this._clock = new Clock();
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
y_fill: true });
this._lockScreenContentsBox.add_child(this._clock);
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
this._notificationsBox = new NotificationsBox();
this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', this._wakeUpScreen.bind(this));
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
this._lockScreenContentsBox.add_child(this._notificationsBox);
this._hasLockScreen = true;
}
@ -1223,7 +1229,7 @@ var ScreenShield = class {
scale_y: 0,
duration: animate ? Overview.ANIMATION_TIME : 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._completeDeactivate()
onComplete: () => this._completeDeactivate(),
});
}
@ -1233,8 +1239,8 @@ var ScreenShield = class {
this._dialog = null;
}
this._longLightbox.hide();
this._shortLightbox.hide();
this._longLightbox.lightOff();
this._shortLightbox.lightOff();
this.actor.hide();
if (this._becameActiveId != 0) {

View File

@ -1,8 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ScreenshotService */
const { Clutter, Gio, GLib, Meta, Shell, St } = imports.gi;
const Signals = imports.signals;
const { Clutter, Graphene, Gio, GObject, GLib, Meta, Shell, St } = imports.gi;
const GrabHelper = imports.ui.grabHelper;
const Lightbox = imports.ui.lightbox;
@ -65,7 +64,55 @@ var ScreenshotService = class {
y + height <= global.screen_height;
}
_onScreenshotComplete(result, area, filenameUsed, flash, invocation) {
*_resolveRelativeFilename(filename) {
if (GLib.str_has_suffix(filename, '.png'))
filename = filename.substr(0, -4);
let path = [
GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES),
GLib.get_home_dir(),
].find(p => GLib.file_test(p, GLib.FileTest.EXISTS));
if (!path)
return null;
yield Gio.File.new_for_path(
GLib.build_filenamev([path, `${filename}.png`]));
for (let idx = 1; ; idx++) {
yield Gio.File.new_for_path(
GLib.build_filenamev([path, `${filename}-${idx}.png`]));
}
}
_createStream(filename) {
if (filename == '')
return [Gio.MemoryOutputStream.new_resizable(), null];
if (GLib.path_is_absolute(filename)) {
try {
let file = Gio.File.new_for_path(filename);
let stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null);
return [stream, file];
} catch (e) {
return [null, null];
}
}
for (let file of this._resolveRelativeFilename(filename)) {
try {
let stream = file.create(Gio.FileCreateFlags.NONE, null);
return [stream, file];
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
return [null, null];
}
}
return [null, null];
}
_onScreenshotComplete(result, area, stream, file, flash, invocation) {
if (result) {
if (flash) {
let flashspot = new Flashspot(area);
@ -77,6 +124,17 @@ var ScreenshotService = class {
}
}
stream.close(null);
let filenameUsed = '';
if (file) {
filenameUsed = file.get_path();
} else {
let bytes = stream.steal_as_bytes();
let clipboard = St.Clipboard.get_default();
clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes);
}
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
invocation.return_value(retval);
}
@ -111,15 +169,18 @@ var ScreenshotService = class {
let screenshot = this._createScreenshot(invocation);
if (!screenshot)
return;
screenshot.screenshot_area (x, y, width, height, filename,
let [stream, file] = this._createStream(filename);
screenshot.screenshot_area(x, y, width, height, stream,
(o, res) => {
try {
let [result, area, filenameUsed] =
let [result, area] =
screenshot.screenshot_area_finish(res);
this._onScreenshotComplete(
result, area, filenameUsed, flash, invocation);
result, area, stream, file, flash, invocation);
} catch (e) {
invocation.return_gerror (e);
invocation.return_gerror(e);
}
});
}
@ -129,15 +190,18 @@ var ScreenshotService = class {
let screenshot = this._createScreenshot(invocation);
if (!screenshot)
return;
screenshot.screenshot_window (includeFrame, includeCursor, filename,
let [stream, file] = this._createStream(filename);
screenshot.screenshot_window(includeFrame, includeCursor, stream,
(o, res) => {
try {
let [result, area, filenameUsed] =
let [result, area] =
screenshot.screenshot_window_finish(res);
this._onScreenshotComplete(
result, area, filenameUsed, flash, invocation);
result, area, stream, file, flash, invocation);
} catch (e) {
invocation.return_gerror (e);
invocation.return_gerror(e);
}
});
}
@ -147,15 +211,18 @@ var ScreenshotService = class {
let screenshot = this._createScreenshot(invocation);
if (!screenshot)
return;
screenshot.screenshot(includeCursor, filename,
let [stream, file] = this._createStream(filename);
screenshot.screenshot(includeCursor, stream,
(o, res) => {
try {
let [result, area, filenameUsed] =
let [result, area] =
screenshot.screenshot_finish(res);
this._onScreenshotComplete(
result, area, filenameUsed, flash, invocation);
result, area, stream, file, flash, invocation);
} catch (e) {
invocation.return_gerror (e);
invocation.return_gerror(e);
}
});
}
@ -163,7 +230,7 @@ var ScreenshotService = class {
SelectAreaAsync(params, invocation) {
let selectArea = new SelectArea();
selectArea.show();
selectArea.connect('finished', (selectArea, areaRectangle) => {
selectArea.connect('finished', (o, areaRectangle) => {
if (areaRectangle) {
let retRectangle = this._unscaleArea(areaRectangle.x, areaRectangle.y,
areaRectangle.width, areaRectangle.height);
@ -185,7 +252,7 @@ var ScreenshotService = class {
"Invalid params");
return;
}
let flashspot = new Flashspot({ x: x, y: y, width: width, height: height });
let flashspot = new Flashspot({ x, y, width, height });
flashspot.fire();
invocation.return_value(null);
}
@ -193,20 +260,20 @@ var ScreenshotService = class {
PickColorAsync(params, invocation) {
let pickPixel = new PickPixel();
pickPixel.show();
pickPixel.connect('finished', (pickPixel, coords) => {
pickPixel.connect('finished', (obj, coords) => {
if (coords) {
let screenshot = this._createScreenshot(invocation, false);
if (!screenshot)
return;
screenshot.pick_color(...coords, (o, res) => {
screenshot.pick_color(coords.x, coords.y, (_o, res) => {
let [success_, color] = screenshot.pick_color_finish(res);
let { red, green, blue } = color;
let retval = GLib.Variant.new('(a{sv})', [{
color: GLib.Variant.new('(ddd)', [
red / 255.0,
green / 255.0,
blue / 255.0
])
blue / 255.0,
]),
}]);
this._removeShooterForSender(invocation.get_sender());
invocation.return_value(retval);
@ -219,62 +286,61 @@ var ScreenshotService = class {
}
};
var SelectArea = class {
constructor() {
var SelectArea = GObject.registerClass({
Signals: { 'finished': { param_types: [Meta.Rectangle.$gtype] } },
}, class SelectArea extends St.Widget {
_init() {
this._startX = -1;
this._startY = -1;
this._lastX = 0;
this._lastY = 0;
this._result = null;
this._group = new St.Widget({ visible: false,
reactive: true,
x: 0,
y: 0 });
Main.uiGroup.add_actor(this._group);
super._init({
visible: false,
reactive: true,
x: 0,
y: 0,
});
Main.uiGroup.add_actor(this);
this._grabHelper = new GrabHelper.GrabHelper(this._group);
this._group.connect('button-press-event',
this._onButtonPress.bind(this));
this._group.connect('button-release-event',
this._onButtonRelease.bind(this));
this._group.connect('motion-event',
this._onMotionEvent.bind(this));
this._grabHelper = new GrabHelper.GrabHelper(this);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._group.add_constraint(constraint);
this.add_constraint(constraint);
this._rubberband = new St.Widget({
style_class: 'select-area-rubberband',
visible: false
visible: false,
});
this._group.add_actor(this._rubberband);
this.add_actor(this._rubberband);
}
show() {
if (!this._grabHelper.grab({ actor: this._group,
vfunc_show() {
if (!this._grabHelper.grab({ actor: this,
onUngrab: this._onUngrab.bind(this) }))
return;
global.display.set_cursor(Meta.Cursor.CROSSHAIR);
Main.uiGroup.set_child_above_sibling(this._group, null);
this._group.visible = true;
Main.uiGroup.set_child_above_sibling(this, null);
super.vfunc_show();
}
_getGeometry() {
return { x: Math.min(this._startX, this._lastX),
y: Math.min(this._startY, this._lastY),
width: Math.abs(this._startX - this._lastX) + 1,
height: Math.abs(this._startY - this._lastY) + 1 };
return new Meta.Rectangle({
x: Math.min(this._startX, this._lastX),
y: Math.min(this._startY, this._lastY),
width: Math.abs(this._startX - this._lastX) + 1,
height: Math.abs(this._startY - this._lastY) + 1,
});
}
_onMotionEvent(actor, event) {
vfunc_motion_event(motionEvent) {
if (this._startX == -1 || this._startY == -1 || this._result)
return Clutter.EVENT_PROPAGATE;
[this._lastX, this._lastY] = event.get_coords();
[this._lastX, this._lastY] = [motionEvent.x, motionEvent.y];
this._lastX = Math.floor(this._lastX);
this._lastY = Math.floor(this._lastY);
let geometry = this._getGeometry();
@ -286,8 +352,8 @@ var SelectArea = class {
return Clutter.EVENT_PROPAGATE;
}
_onButtonPress(actor, event) {
[this._startX, this._startY] = event.get_coords();
vfunc_button_press_event(buttonEvent) {
[this._startX, this._startY] = [buttonEvent.x, buttonEvent.y];
this._startX = Math.floor(this._startX);
this._startY = Math.floor(this._startY);
this._rubberband.set_position(this._startX, this._startY);
@ -295,13 +361,13 @@ var SelectArea = class {
return Clutter.EVENT_PROPAGATE;
}
_onButtonRelease() {
vfunc_button_release_event() {
this._result = this._getGeometry();
this._group.ease({
this.ease({
opacity: 0,
duration: 200,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._grabHelper.ungrab()
onComplete: () => this._grabHelper.ungrab(),
});
return Clutter.EVENT_PROPAGATE;
}
@ -311,43 +377,42 @@ var SelectArea = class {
this.emit('finished', this._result);
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this._group.destroy();
this.destroy();
return GLib.SOURCE_REMOVE;
});
}
};
Signals.addSignalMethods(SelectArea.prototype);
});
var PickPixel = GObject.registerClass({
Signals: { 'finished': { param_types: [Graphene.Point.$gtype] } },
}, class PickPixel extends St.Widget {
_init() {
super._init({ visible: false, reactive: true });
var PickPixel = class {
constructor() {
this._result = null;
this._group = new St.Widget({ visible: false,
reactive: true });
Main.uiGroup.add_actor(this._group);
Main.uiGroup.add_actor(this);
this._grabHelper = new GrabHelper.GrabHelper(this._group);
this._group.connect('button-release-event',
this._onButtonRelease.bind(this));
this._grabHelper = new GrabHelper.GrabHelper(this);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._group.add_constraint(constraint);
this.add_constraint(constraint);
}
show() {
if (!this._grabHelper.grab({ actor: this._group,
vfunc_show() {
if (!this._grabHelper.grab({ actor: this,
onUngrab: this._onUngrab.bind(this) }))
return;
global.display.set_cursor(Meta.Cursor.CROSSHAIR);
Main.uiGroup.set_child_above_sibling(this._group, null);
this._group.visible = true;
Main.uiGroup.set_child_above_sibling(this, null);
super.vfunc_show();
}
_onButtonRelease(actor, event) {
this._result = event.get_coords();
vfunc_button_release_event(buttonEvent) {
let { x, y } = buttonEvent;
this._result = new Graphene.Point({ x, y });
this._grabHelper.ungrab();
return Clutter.EVENT_PROPAGATE;
}
@ -357,29 +422,29 @@ var PickPixel = class {
this.emit('finished', this._result);
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this._group.destroy();
this.destroy();
return GLib.SOURCE_REMOVE;
});
}
};
Signals.addSignalMethods(PickPixel.prototype);
});
var FLASHSPOT_ANIMATION_OUT_TIME = 500; // milliseconds
var Flashspot = class extends Lightbox.Lightbox {
constructor(area) {
super(Main.uiGroup, { inhibitEvents: true,
width: area.width,
height: area.height });
this.actor.style_class = 'flashspot';
this.actor.set_position(area.x, area.y);
var Flashspot = GObject.registerClass(
class Flashspot extends Lightbox.Lightbox {
_init(area) {
super._init(Main.uiGroup, {
inhibitEvents: true,
width: area.width,
height: area.height,
});
this.style_class = 'flashspot';
this.set_position(area.x, area.y);
}
fire(doneCallback) {
this.actor.show();
this.actor.opacity = 255;
this.actor.ease({
this.set({ visible: true, opacity: 255 });
this.ease({
opacity: 0,
duration: FLASHSPOT_ANIMATION_OUT_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
@ -387,7 +452,7 @@ var Flashspot = class extends Lightbox.Lightbox {
if (doneCallback)
doneCallback();
this.destroy();
}
},
});
}
};
});

View File

@ -32,7 +32,8 @@ const { loadInterfaceXML } = imports.misc.fileUtils;
/**
* sleep:
* @milliseconds: number of milliseconds to wait
* @param {number} milliseconds - number of milliseconds to wait
* @returns {Promise} that resolves after @milliseconds ms
*
* Used within an automation script to pause the the execution of the
* current script for the specified amount of time. Use as
@ -50,6 +51,7 @@ function sleep(milliseconds) {
/**
* waitLeisure:
* @returns {Promise} that resolves when the shell is idle
*
* Used within an automation script to pause the the execution of the
* current script until the shell is completely idle. Use as
@ -90,13 +92,13 @@ function _callRemote(obj, method, ...args) {
/**
* createTestWindow:
* @params: options for window creation.
* width - width of window, in pixels (default 640)
* height - height of window, in pixels (default 480)
* alpha - whether the window should have an alpha channel (default false)
* maximized - whether the window should be created maximized (default false)
* redraws - whether the window should continually redraw itself (default false)
* @maximized: whethe the window should be created maximized
* @param {Object} params: options for window creation.
* {number} [params.width=640] - width of window, in pixels
* {number} [params.height=480] - height of window, in pixels
* {bool} [params.alpha=false] - whether the window should have an alpha channel
* {bool} [params.maximized=false] - whether the window should be created maximized
* {bool} [params.redraws=false] - whether the window should continually redraw itself
* @returns {Promise}
*
* Creates a window using gnome-shell-perf-helper for testing purposes.
* While this function can be used with yield in an automation
@ -119,6 +121,7 @@ function createTestWindow(params) {
/**
* waitTestWindows:
* @returns {Promise}
*
* Used within an automation script to pause until all windows previously
* created with createTestWindow have been mapped and exposed.
@ -130,6 +133,7 @@ function waitTestWindows() {
/**
* destroyTestWindows:
* @returns {Promise}
*
* Destroys all windows previously created with createTestWindow().
* While this function can be used with yield in an automation
@ -144,8 +148,8 @@ function destroyTestWindows() {
/**
* defineScriptEvent
* @name: The event will be called script.<name>
* @description: Short human-readable description of the event
* @param {string} name: The event will be called script.<name>
* @param {string} description: Short human-readable description of the event
*
* Convenience function to define a zero-argument performance event
* within the 'script' namespace that is reserved for events defined locally
@ -159,7 +163,7 @@ function defineScriptEvent(name, description) {
/**
* scriptEvent
* @name: Name registered with defineScriptEvent()
* @param {string} name: Name registered with defineScriptEvent()
*
* Convenience function to record a script-local performance event
* previously defined with defineScriptEvent
@ -200,8 +204,8 @@ function _collect(scriptModule, outputFile) {
let raw = f.replace(null, false,
Gio.FileCreateFlags.NONE,
null);
let out = Gio.BufferedOutputStream.new_sized (raw, 4096);
Shell.write_string_to_stream (out, "{\n");
let out = Gio.BufferedOutputStream.new_sized(raw, 4096);
Shell.write_string_to_stream(out, "{\n");
Shell.write_string_to_stream(out, '"events":\n');
Shell.PerfLog.get_default().dump_events(out);
@ -250,10 +254,10 @@ function _collect(scriptModule, outputFile) {
}
Shell.write_string_to_stream(out, ' ]');
Shell.write_string_to_stream (out, ',\n"log":\n');
Shell.write_string_to_stream(out, ',\n"log":\n');
Shell.PerfLog.get_default().dump_log(out);
Shell.write_string_to_stream (out, '\n}\n');
Shell.write_string_to_stream(out, '\n}\n');
out.close(null);
} else {
let metrics = [];
@ -262,20 +266,21 @@ function _collect(scriptModule, outputFile) {
metrics.sort();
print ('------------------------------------------------------------');
print('------------------------------------------------------------');
for (let i = 0; i < metrics.length; i++) {
let metric = metrics[i];
print (`# ${scriptModule.METRICS[metric].description}`);
print (`${metric}: ${scriptModule.METRICS[metric].value}${scriptModule.METRICS[metric].units}`);
print(`# ${scriptModule.METRICS[metric].description}`);
print(`${metric}: ${scriptModule.METRICS[metric].value}${scriptModule.METRICS[metric].units}`);
}
print ('------------------------------------------------------------');
print('------------------------------------------------------------');
}
}
/**
* runPerfScript
* @scriptModule: module object with run and finish functions
* and event handlers
* @param {Object} scriptModule: module object with run and finish
* functions and event handlers
* @param {string} outputFile: path to write output to
*
* Runs a script for automated collection of performance data. The
* script is defined as a Javascript module with specified contents.

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported SearchResultsView */
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
const Signals = imports.signals;
const AppDisplay = imports.ui.appDisplay;
const IconGrid = imports.ui.iconGrid;
@ -32,69 +32,79 @@ class MaxWidthBox extends St.BoxLayout {
}
});
var SearchResult = class {
constructor(provider, metaInfo, resultsView) {
var SearchResult = GObject.registerClass(
class SearchResult extends St.Button {
_init(provider, metaInfo, resultsView) {
this.provider = provider;
this.metaInfo = metaInfo;
this._resultsView = resultsView;
this.actor = new St.Button({ reactive: true,
can_focus: true,
track_hover: true,
x_align: St.Align.START,
y_fill: true });
super._init({
reactive: true,
can_focus: true,
track_hover: true,
});
}
this.actor._delegate = this;
this.actor.connect('clicked', this.activate.bind(this));
vfunc_clicked() {
this.activate();
}
activate() {
this.emit('activate', this.metaInfo.id);
this.provider.activateResult(this.metaInfo.id, this._resultsView.terms);
if (this.metaInfo.clipboardText) {
St.Clipboard.get_default().set_text(
St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText);
}
Main.overview.toggle();
}
};
Signals.addSignalMethods(SearchResult.prototype);
});
var ListSearchResult = class extends SearchResult {
var ListSearchResult = GObject.registerClass(
class ListSearchResult extends SearchResult {
_init(provider, metaInfo, resultsView) {
super._init(provider, metaInfo, resultsView);
constructor(provider, metaInfo, resultsView) {
super(provider, metaInfo, resultsView);
this.style_class = 'list-search-result';
this.actor.style_class = 'list-search-result';
this.actor.x_fill = true;
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
vertical: false });
this.actor.set_child(content);
let content = new St.BoxLayout({
style_class: 'list-search-result-content',
vertical: false,
x_align: Clutter.ActorAlign.START,
x_expand: true,
y_expand: true,
});
this.set_child(content);
this._termsChangedId = 0;
let titleBox = new St.BoxLayout({ style_class: 'list-search-result-title' });
let titleBox = new St.BoxLayout({
style_class: 'list-search-result-title',
y_align: Clutter.ActorAlign.CENTER,
});
content.add(titleBox, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
content.add_child(titleBox);
// An icon for, or thumbnail of, content
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
if (icon) {
if (icon)
titleBox.add(icon);
}
let title = new St.Label({ text: this.metaInfo['name'] });
titleBox.add(title, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let title = new St.Label({
text: this.metaInfo['name'],
y_align: Clutter.ActorAlign.CENTER,
});
titleBox.add_child(title);
this.actor.label_actor = title;
this.label_actor = title;
if (this.metaInfo['description']) {
this._descriptionLabel = new St.Label({ style_class: 'list-search-result-description' });
content.add(this._descriptionLabel, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this._descriptionLabel = new St.Label({
style_class: 'list-search-result-description',
y_align: Clutter.ActorAlign.CENTER,
});
content.add_child(this._descriptionLabel);
this._termsChangedId =
this._resultsView.connect('terms-changed',
@ -103,7 +113,7 @@ var ListSearchResult = class extends SearchResult {
this._highlightTerms();
}
this.actor.connect('destroy', this._onDestroy.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
}
get ICON_SIZE() {
@ -120,50 +130,57 @@ var ListSearchResult = class extends SearchResult {
this._resultsView.disconnect(this._termsChangedId);
this._termsChangedId = 0;
}
};
});
var GridSearchResult = class extends SearchResult {
constructor(provider, metaInfo, resultsView) {
super(provider, metaInfo, resultsView);
var GridSearchResult = GObject.registerClass(
class GridSearchResult extends SearchResult {
_init(provider, metaInfo, resultsView) {
super._init(provider, metaInfo, resultsView);
this.actor.style_class = 'grid-search-result';
this.style_class = 'grid-search-result';
this.icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
let content = new St.Bin({ child: this.icon });
this.actor.set_child(content);
this.actor.label_actor = this.icon.label;
let content = new St.Bin({
child: this.icon,
x_align: Clutter.ActorAlign.START,
x_expand: true,
y_expand: true,
});
this.set_child(content);
this.label_actor = this.icon.label;
}
};
});
var SearchResultsBase = GObject.registerClass({
GTypeFlags: GObject.TypeFlags.ABSTRACT,
Properties: {
'focus-child': GObject.ParamSpec.object(
'focus-child', 'focus-child', 'focus-child',
GObject.ParamFlags.READABLE,
Clutter.Actor.$gtype),
},
}, class SearchResultsBase extends St.BoxLayout {
_init(provider, resultsView) {
super._init({ style_class: 'search-section', vertical: true });
var SearchResultsBase = class {
constructor(provider, resultsView) {
this.provider = provider;
this._resultsView = resultsView;
this._terms = [];
this._focusChild = null;
this.actor = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
this._resultDisplayBin = new St.Bin({ x_fill: true,
y_fill: true });
this.actor.add(this._resultDisplayBin, { expand: true });
this._resultDisplayBin = new St.Bin();
this.add_child(this._resultDisplayBin);
let separator = new St.Widget({ style_class: 'search-section-separator' });
this.actor.add(separator);
this.add(separator);
this._resultDisplays = {};
this._clipboard = St.Clipboard.get_default();
this._cancellable = new Gio.Cancellable();
this.actor.connect('destroy', this._onDestroy.bind(this));
}
destroy() {
this.actor.destroy();
this.connect('destroy', this._onDestroy.bind(this));
}
_onDestroy() {
@ -180,21 +197,21 @@ var SearchResultsBase = class {
clear() {
this._cancellable.cancel();
for (let resultId in this._resultDisplays)
this._resultDisplays[resultId].actor.destroy();
this._resultDisplays[resultId].destroy();
this._resultDisplays = {};
this._clearResultDisplay();
this.actor.hide();
this.hide();
}
get focusChild() {
return this._focusChild;
}
_keyFocusIn(actor) {
this.emit('key-focus-in', actor);
}
_activateResult(result, id) {
this.provider.activateResult(id, this._terms);
if (result.metaInfo.clipboardText)
this._clipboard.set_text(St.ClipboardType.CLIPBOARD, result.metaInfo.clipboardText);
Main.overview.toggle();
if (this._focusChild == actor)
return;
this._focusChild = actor;
this.notify('focus-child');
}
_setMoreCount(_count) {
@ -233,8 +250,7 @@ var SearchResultsBase = class {
metasNeeded.forEach((resultId, i) => {
let meta = metas[i];
let display = this._createResultDisplay(meta);
display.connect('activate', this._activateResult.bind(this));
display.actor.connect('key-focus-in', this._keyFocusIn.bind(this));
display.connect('key-focus-in', this._keyFocusIn.bind(this));
this._resultDisplays[resultId] = display;
});
callback(true);
@ -246,7 +262,7 @@ var SearchResultsBase = class {
this._terms = terms;
if (providerResults.length == 0) {
this._clearResultDisplay();
this.actor.hide();
this.hide();
callback();
} else {
let maxResults = this._getMaxDisplayedResults();
@ -265,22 +281,23 @@ var SearchResultsBase = class {
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this.actor.hide();
this.hide();
this._clearResultDisplay();
results.forEach(resultId => {
this._addItem(this._resultDisplays[resultId]);
});
this._setMoreCount(this.provider.canLaunchSearch ? moreCount : 0);
this.actor.show();
this.show();
callback();
});
}
}
};
});
var ListSearchResults = class extends SearchResultsBase {
constructor(provider, resultsView) {
super(provider, resultsView);
var ListSearchResults = GObject.registerClass(
class ListSearchResults extends SearchResultsBase {
_init(provider, resultsView) {
super._init(provider, resultsView);
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
this.providerInfo = new ProviderInfo(provider);
@ -291,14 +308,14 @@ var ListSearchResults = class extends SearchResultsBase {
Main.overview.toggle();
});
this._container.add(this.providerInfo, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._container.add_child(this.providerInfo);
this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true });
this._container.add(this._content, { expand: true });
this._content = new St.BoxLayout({
style_class: 'list-search-results',
vertical: true,
x_expand: true,
});
this._container.add_child(this._content);
this._resultDisplayBin.set_child(this._container);
}
@ -321,26 +338,26 @@ var ListSearchResults = class extends SearchResultsBase {
}
_addItem(display) {
this._content.add_actor(display.actor);
this._content.add_actor(display);
}
getFirstResult() {
if (this._content.get_n_children() > 0)
return this._content.get_child_at_index(0)._delegate;
return this._content.get_child_at_index(0);
else
return null;
}
};
Signals.addSignalMethods(ListSearchResults.prototype);
});
var GridSearchResults = class extends SearchResultsBase {
constructor(provider, resultsView) {
super(provider, resultsView);
var GridSearchResults = GObject.registerClass(
class GridSearchResults extends SearchResultsBase {
_init(provider, resultsView) {
super._init(provider, resultsView);
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
this._bin = new St.Bin({ x_align: Clutter.ActorAlign.CENTER });
this._bin.set_child(this._grid);
this._resultDisplayBin.set_child(this._bin);
@ -357,7 +374,7 @@ var GridSearchResults = class extends SearchResultsBase {
updateSearch(...args) {
if (this._notifyAllocationId)
this.actor.disconnect(this._notifyAllocationId);
this.disconnect(this._notifyAllocationId);
if (this._updateSearchLater) {
Meta.later_remove(this._updateSearchLater);
delete this._updateSearchLater;
@ -365,7 +382,7 @@ var GridSearchResults = class extends SearchResultsBase {
// Make sure the maximum number of results calculated by
// _getMaxDisplayedResults() is updated after width changes.
this._notifyAllocationId = this.actor.connect('notify::allocation', () => {
this._notifyAllocationId = this.connect('notify::allocation', () => {
if (this._updateSearchLater)
return;
this._updateSearchLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
@ -379,7 +396,7 @@ var GridSearchResults = class extends SearchResultsBase {
}
_getMaxDisplayedResults() {
let width = this.actor.allocation.x2 - this.actor.allocation.x1;
let width = this.allocation.get_width();
if (width == 0)
return -1;
@ -402,25 +419,30 @@ var GridSearchResults = class extends SearchResultsBase {
getFirstResult() {
if (this._grid.visibleItemsCount() > 0)
return this._grid.getItemAtIndex(0)._delegate;
return this._grid.getItemAtIndex(0);
else
return null;
}
};
Signals.addSignalMethods(GridSearchResults.prototype);
});
var SearchResults = class {
constructor() {
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
var SearchResultsView = GObject.registerClass({
Signals: { 'terms-changed': {} },
}, class SearchResultsView extends St.BoxLayout {
_init() {
super._init({ name: 'searchResults', vertical: true });
this._content = new MaxWidthBox({ name: 'searchResultsContent',
vertical: true });
this._content = new MaxWidthBox({
name: 'searchResultsContent',
vertical: true,
x_expand: true,
});
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
overlay_scrollbars: true,
style_class: 'search-display vfade' });
this._scrollView = new St.ScrollView({
overlay_scrollbars: true,
style_class: 'search-display vfade',
x_expand: true,
y_expand: true,
});
this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
this._scrollView.add_actor(this._content);
@ -428,16 +450,15 @@ var SearchResults = class {
action.connect('pan', this._onPan.bind(this));
this._scrollView.add_action(action);
this.actor.add(this._scrollView, { x_fill: true,
y_fill: true,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this.add_child(this._scrollView);
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this.actor.add(this._statusBin, { expand: true });
this._statusText = new St.Label({
style_class: 'search-statustext',
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER,
});
this._statusBin = new St.Bin({ y_expand: true });
this.add_child(this._statusBin);
this._statusBin.add_actor(this._statusText);
this._highlightDefault = false;
@ -467,6 +488,10 @@ var SearchResults = class {
this._reloadRemoteProviders();
}
get terms() {
return this._terms;
}
_reloadRemoteProviders() {
let remoteProviders = this._providers.filter(p => p.isRemoteProvider);
remoteProviders.forEach(provider => {
@ -525,19 +550,20 @@ var SearchResults = class {
provider.searchInProgress = true;
let previousProviderResults = previousResults[provider.id];
if (this._isSubSearch && previousProviderResults)
if (this._isSubSearch && previousProviderResults) {
provider.getSubsearchResultSet(previousProviderResults,
this._terms,
results => {
this._gotResults(results, provider);
},
this._cancellable);
else
} else {
provider.getInitialResultSet(this._terms,
results => {
this._gotResults(results, provider);
},
this._cancellable);
}
});
this._updateSearchProgress();
@ -591,12 +617,12 @@ var SearchResults = class {
_onPan(action) {
let [dist_, dx_, dy] = action.get_motion_delta(0);
let adjustment = this._scrollView.vscroll.adjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
adjustment.value -= (dy / this.height) * adjustment.page_size;
return false;
}
_keyFocusIn(provider, actor) {
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
_focusChildChanged(provider) {
Util.ensureActorVisibleInScrollView(this._scrollView, provider.focusChild);
}
_ensureProviderDisplay(provider) {
@ -609,9 +635,9 @@ var SearchResults = class {
else
providerDisplay = new GridSearchResults(provider, this);
providerDisplay.connect('key-focus-in', this._keyFocusIn.bind(this));
providerDisplay.actor.hide();
this._content.add(providerDisplay.actor);
providerDisplay.connect('notify::focus-child', this._focusChildChanged.bind(this));
providerDisplay.hide();
this._content.add(providerDisplay);
provider.display = providerDisplay;
}
@ -629,7 +655,7 @@ var SearchResults = class {
let provider = providers[i];
let display = provider.display;
if (!display.actor.visible)
if (!display.visible)
continue;
let firstResult = display.getFirstResult();
@ -657,18 +683,17 @@ var SearchResults = class {
_updateSearchProgress() {
let haveResults = this._providers.some(provider => {
let display = provider.display;
return (display.getFirstResult() != null);
return display.getFirstResult() != null;
});
this._scrollView.visible = haveResults;
this._statusBin.visible = !haveResults;
if (!haveResults) {
if (this.searchInProgress) {
if (this.searchInProgress)
this._statusText.set_text(_("Searching…"));
} else {
else
this._statusText.set_text(_("No results."));
}
}
}
@ -704,22 +729,22 @@ var SearchResults = class {
this._doSearch();
if (this._defaultResult)
this._defaultResult.actor.popup_menu();
this._defaultResult.popup_menu();
}
navigateFocus(direction) {
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
let rtl = this.get_text_direction() == Clutter.TextDirection.RTL;
if (direction == St.DirectionType.TAB_BACKWARD ||
direction == (rtl
? St.DirectionType.RIGHT
: St.DirectionType.LEFT) ||
direction == St.DirectionType.UP) {
this.actor.navigate_focus(null, direction, false);
this.navigate_focus(null, direction, false);
return;
}
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, direction, false);
let from = this._defaultResult ? this._defaultResult : null;
this.navigate_focus(from, direction, false);
}
_setSelected(result, selected) {
@ -727,10 +752,10 @@ var SearchResults = class {
return;
if (selected) {
result.actor.add_style_pseudo_class('selected');
Util.ensureActorVisibleInScrollView(this._scrollView, result.actor);
result.add_style_pseudo_class('selected');
Util.ensureActorVisibleInScrollView(this._scrollView, result);
} else {
result.actor.remove_style_pseudo_class('selected');
result.remove_style_pseudo_class('selected');
}
}
@ -743,18 +768,20 @@ var SearchResults = class {
return description.replace(this._highlightRegex, '<b>$1</b>');
}
};
Signals.addSignalMethods(SearchResults.prototype);
});
var ProviderInfo = GObject.registerClass(
class ProviderInfo extends St.Button {
_init(provider) {
this.provider = provider;
super._init({ style_class: 'search-provider-icon',
reactive: true,
can_focus: true,
accessible_name: provider.appInfo.get_name(),
track_hover: true });
super._init({
style_class: 'search-provider-icon',
reactive: true,
can_focus: true,
accessible_name: provider.appInfo.get_name(),
track_hover: true,
y_align: Clutter.ActorAlign.START,
});
this._content = new St.BoxLayout({ vertical: false,
style_class: 'list-search-provider-content' });

Some files were not shown because too many files have changed in this diff Show More