Compare commits
488 Commits
3.5.91
...
wip/re-sea
Author | SHA1 | Date | |
---|---|---|---|
555d45f06c | |||
0a8713770b | |||
7d3ea1ac68 | |||
7785710964 | |||
3730dc01cf | |||
66bf0df737 | |||
4590b33f2e | |||
6538f60322 | |||
4b0ba8b7b8 | |||
bc8965fe63 | |||
e7fcce3484 | |||
b1e4d3335c | |||
a3a3f24ed3 | |||
96191a9c96 | |||
f4814d200b | |||
52156930d3 | |||
cbaa999ced | |||
9491f6bd23 | |||
afe8198d4b | |||
6aa8f14285 | |||
e073670c4d | |||
17a3d2c63f | |||
ca38e05ed4 | |||
9841e56ebf | |||
c0d3a14ac2 | |||
d485fcf9ec | |||
ce2c5106f8 | |||
5e96c3dfb4 | |||
a72b642f3e | |||
8507d3c4e4 | |||
c985fdccba | |||
1d7c2b1c26 | |||
ab60c628e7 | |||
2c34c8e20f | |||
d19fa731d6 | |||
59bb1cc387 | |||
56909d0646 | |||
04da29c939 | |||
307f7a3024 | |||
e80bfa39f5 | |||
29714922ea | |||
88192114ac | |||
9d78208b76 | |||
d817bf0395 | |||
4d51056226 | |||
a607174a25 | |||
37d6a624b7 | |||
0c807bddaf | |||
a0470bfc66 | |||
b9463d23e8 | |||
04debd1623 | |||
9d31576cf5 | |||
8daca28a90 | |||
9899604261 | |||
687e1eabed | |||
eb09f34114 | |||
fbeb446ed7 | |||
92033ce0f5 | |||
9171bab5e5 | |||
f9819eb7b0 | |||
85728f0d15 | |||
9396849d56 | |||
6f5e5672bb | |||
b936e60876 | |||
fa4bd91213 | |||
81eeef7d3c | |||
a7b5134820 | |||
92a01c67ba | |||
71c23613b5 | |||
4f876995de | |||
9cf4a76196 | |||
9efe5287e4 | |||
8c4b34de4e | |||
93e3559dc3 | |||
caa0f63e1f | |||
a4e29e1244 | |||
599f2f43e3 | |||
3a453c5f73 | |||
98b313c75f | |||
8b796d80a7 | |||
719a8908a6 | |||
fed007ecae | |||
990443465f | |||
1f183b8a4e | |||
96556eb959 | |||
3ffeeac577 | |||
d7929a2340 | |||
667019a8c1 | |||
d9b46b4782 | |||
e249218a9d | |||
a2b1946b01 | |||
96a80f7ba0 | |||
9955fbf4f8 | |||
14966b0cd0 | |||
6e1a8f16a8 | |||
d106191e6a | |||
418cf6281e | |||
bdeb7d86b6 | |||
a0e0cc1038 | |||
78f6dec73b | |||
f6458f215f | |||
c7e0d547c4 | |||
fa54cfa0c3 | |||
31ea3f737c | |||
2a8a8065a8 | |||
d3ba002313 | |||
f18fd8d959 | |||
d54f7b13fb | |||
9c8b75206c | |||
15273c7f22 | |||
0ea8217c55 | |||
1735f28f5a | |||
8de4070f7f | |||
45e64f453f | |||
62dc5f2ac6 | |||
86c85a752e | |||
3de0ebf7fd | |||
1496ba0bbd | |||
f5974f6793 | |||
928ea3bb01 | |||
b7acb1d488 | |||
360d94dd67 | |||
671a813135 | |||
c005417f9b | |||
b5ec68bda3 | |||
b4ed29094d | |||
284ab031b8 | |||
6ad5064c09 | |||
3a8e723a36 | |||
dd3484b93f | |||
205773b700 | |||
f2039070e6 | |||
cf58a7eafd | |||
487749c25b | |||
48fb16b570 | |||
f94369dd6e | |||
fc0bd3b9e8 | |||
19946f1d19 | |||
562f56130a | |||
99f97adfc6 | |||
5ad7db722d | |||
a7d344d287 | |||
0ac215f9de | |||
4342155748 | |||
b561694bf0 | |||
cae9a8d608 | |||
88fbdba018 | |||
046067565a | |||
11299d9913 | |||
42ab233b08 | |||
4a92d7d1b2 | |||
2502ca6ccc | |||
651030ba93 | |||
8dfea9566e | |||
8cc54ce2a2 | |||
3abfcda8b5 | |||
cf0ae8f182 | |||
d90bf5c6dc | |||
955b550e95 | |||
b3cd46a5c8 | |||
3fdc8bfa3d | |||
e8f96a6e16 | |||
dc15df1aa7 | |||
05f5fac35b | |||
5bfcc5392d | |||
d2e830cce3 | |||
1e942be639 | |||
576009bad0 | |||
f71108a214 | |||
7109bd52f2 | |||
0f6effa263 | |||
8b0301ed00 | |||
4f56fb125e | |||
6487cd8c6f | |||
06e5c25383 | |||
36e5ae4a25 | |||
245e43ea8c | |||
52871c781a | |||
f994ada576 | |||
147a6e49dc | |||
be24ee435c | |||
9fac285b69 | |||
3ed5f9cd15 | |||
17e70ff8ec | |||
6dab119650 | |||
f80eb89d57 | |||
f6c2902fe4 | |||
1e890a8a0a | |||
aa1a84e677 | |||
9395f310d6 | |||
b99bb3d4bb | |||
4f66f096ff | |||
7f5f2284f3 | |||
59f9f4fca1 | |||
55284e418c | |||
20d4ffde6e | |||
78e5d4df9d | |||
1118ec9653 | |||
7d6c85be42 | |||
0192a6cb12 | |||
0e01a81219 | |||
36edf20273 | |||
a1bf19dbdf | |||
0ad739e78b | |||
ff9509b901 | |||
7e496b1979 | |||
448517032e | |||
a171e92e6c | |||
06e9bf9b0a | |||
7d9ec8cea0 | |||
5b4553ff0c | |||
d4ce7aef59 | |||
8b6df2e23f | |||
e2ff5846df | |||
24efeff788 | |||
4f359e62df | |||
e6fd2bed4d | |||
9fb6510135 | |||
7206b61838 | |||
e8ab0b3e8f | |||
843788580e | |||
4ceb3d890d | |||
3f8995b25e | |||
37e0cefc79 | |||
f026741dbb | |||
044b121e01 | |||
d57c3b4f89 | |||
960f7d5f2e | |||
2f61381651 | |||
ba6e931e21 | |||
440aa0d369 | |||
f42d4b5fa2 | |||
5d9fa2c484 | |||
f5c86fa171 | |||
f9019ce62d | |||
ee485e1728 | |||
8f2a6f8387 | |||
908bf3b117 | |||
6a739afd25 | |||
506490e32d | |||
846771f2a1 | |||
759b7584e0 | |||
e5cb224598 | |||
e32df1b405 | |||
db9f91b687 | |||
0941357068 | |||
d8993c52d0 | |||
6424b2dd03 | |||
2a4eb3ed1a | |||
c9fa00cce1 | |||
18eedbc02d | |||
ef9f63fe59 | |||
f8ce788425 | |||
6c1bd95643 | |||
2ed7ee8f71 | |||
2e63709450 | |||
6611d639a8 | |||
fe124e6ab3 | |||
fee0a8527d | |||
2f6b00403f | |||
dbe2c117e3 | |||
c4b1ccb6d6 | |||
2c70ee7e43 | |||
f7826616b8 | |||
9f476a12dd | |||
6f3cf0ae50 | |||
cd024e21f0 | |||
c4c470c1f3 | |||
dc9ad8df80 | |||
74d6225993 | |||
e1e0c5035d | |||
adf6d0eb82 | |||
90df435345 | |||
83d57211db | |||
0a8eeb2827 | |||
0c324c42f4 | |||
3adf54a952 | |||
8076c66a4c | |||
f9c583a636 | |||
30d536b19c | |||
1957899146 | |||
b52c83d88a | |||
27ff388413 | |||
d4306f7768 | |||
5d0a57c97d | |||
a87ba467ae | |||
7df7cd01eb | |||
098d805a8b | |||
4a21034a00 | |||
7904e359f2 | |||
0c8a94beb8 | |||
c13a573792 | |||
f9b42e12ae | |||
5c2031b768 | |||
49643882d4 | |||
175ddaa3a1 | |||
cc1d6e97b8 | |||
79b96ab301 | |||
44d9316023 | |||
5b0c9a74fb | |||
3429fc3e4c | |||
d11d8d5353 | |||
1742bd6ded | |||
9f6e118ea0 | |||
717bbd3bb5 | |||
e8ebe4de14 | |||
0ff614ccd4 | |||
d0a77b7e0c | |||
2815889090 | |||
1317956663 | |||
a2303d8895 | |||
2161c793dc | |||
a2f943db8f | |||
23a31b9c00 | |||
2591d1b94b | |||
300f97f102 | |||
7e3f6c3066 | |||
601d232064 | |||
8854ac84ad | |||
0302c3fbd4 | |||
6b6fdc6cfe | |||
7126ce86ec | |||
760bf52f75 | |||
6d791d31de | |||
21adc98d70 | |||
6fd7a56568 | |||
fba427fcaf | |||
7753361ffb | |||
c1590d9ed7 | |||
23c1138a58 | |||
2acb097662 | |||
449d116af2 | |||
110240981d | |||
ac5c6de929 | |||
51bdc44352 | |||
bafe34696d | |||
114f6f577f | |||
1f30670c1d | |||
10884ef7f5 | |||
5e12e5f42a | |||
e7a2e1f268 | |||
cc6744055f | |||
e6ba7c6e40 | |||
ea4855e908 | |||
5c7da4b0e6 | |||
9659d934ac | |||
de93677271 | |||
6ef3c628e6 | |||
f2af0be9ac | |||
ef7b74a104 | |||
906368d916 | |||
809cbf58c6 | |||
a5d60050a2 | |||
2c130c8668 | |||
c0ff02d9c7 | |||
cb627db392 | |||
33bd6bc75b | |||
ea5bf109cb | |||
b150a2842e | |||
2a46c39019 | |||
5ca5f026c5 | |||
15f22add79 | |||
131fa6a359 | |||
2d184e1842 | |||
95a55a2c1c | |||
8eecbb5c17 | |||
abfdcaa1b5 | |||
8ee74e5661 | |||
ff31ccdd30 | |||
f6645a41d2 | |||
daceb8105f | |||
4beba796d7 | |||
70c34baafb | |||
e71c016477 | |||
633bbd8a9e | |||
4c82df56e9 | |||
0d62ec5b03 | |||
38f943ef81 | |||
e421953fcd | |||
f39098a4f2 | |||
b203a95a78 | |||
2bb1a6792a | |||
e30d45a2b0 | |||
afcd90ae27 | |||
94c1d5a18c | |||
4254fa3d38 | |||
3b293e91e3 | |||
3d6320295e | |||
7654f1ca3e | |||
e62c66b153 | |||
638507caff | |||
107f5de58e | |||
b1451523ca | |||
f7a95b5edc | |||
be290fafe7 | |||
b7ae74edb9 | |||
b3041ae9fc | |||
7499b04638 | |||
bf2d2071fc | |||
488820daec | |||
77c15b76b5 | |||
e71129aa68 | |||
6f5b700833 | |||
9627864ca8 | |||
2a8625ffae | |||
ed82c3763c | |||
22266899dc | |||
9dfd1bfa41 | |||
409af28cb7 | |||
4fe604bfe8 | |||
49df72ceda | |||
2db029bcdb | |||
7fd128eabc | |||
5e3d8dd3eb | |||
785be2f327 | |||
8cf9baa132 | |||
09e3aed770 | |||
452ac297ab | |||
7ebb5c6a10 | |||
00e0d24a6a | |||
8a269041fb | |||
9f48adcff9 | |||
48e7d732b9 | |||
30048b74d1 | |||
c866b0dbfd | |||
1c79f18b13 | |||
6d704be88b | |||
5af389c087 | |||
475dde4193 | |||
8c534163e1 | |||
657887b241 | |||
10da35cbef | |||
00f15c1075 | |||
19f92df8ab | |||
8639cf8060 | |||
2501d29e9d | |||
8109dd684e | |||
5a259dd6b0 | |||
2ed28211ed | |||
9d0eaa216f | |||
58477282fe | |||
6b016c2528 | |||
b98c5f94ee | |||
ad8bdb929a | |||
5030d59fcc | |||
ec52928736 | |||
7f479e18e6 | |||
7999e9020d | |||
43ba93a817 | |||
b37e02c90a | |||
be5df17a4a | |||
ffbc1fd190 | |||
457fb604dd | |||
09c81f79f6 | |||
0725a7d836 | |||
f957ae71c3 | |||
011c9d1beb | |||
aed589a98d | |||
3830e90642 | |||
3710b88ab9 | |||
5c990f103e | |||
2890fc9d7d | |||
0223507a2e | |||
59412a9405 | |||
110d58bbc3 | |||
eda17defb2 | |||
f563fb124e | |||
14d0a96999 | |||
f0474ffccc | |||
11ce6845f2 | |||
70d610b5e4 | |||
f5ca649977 | |||
8954d33019 | |||
2e6205bc05 | |||
e0dea63079 | |||
7c244b01c6 | |||
3f5edf7c3e | |||
92d8d65543 | |||
cebd8e14e9 | |||
16e92a7ca3 | |||
59e2710137 | |||
f1ca96bbf0 | |||
ec01f5d5ee | |||
2a800e4ce0 | |||
ca2e09fe8b | |||
7e343f11f2 | |||
db20a54861 | |||
ec4f6b7f91 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,6 +16,7 @@ config.log
|
||||
config.status
|
||||
config
|
||||
configure
|
||||
data/50-gnome-shell-*.xml
|
||||
data/gnome-shell.desktop
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop
|
||||
|
331
HACKING
Normal file
331
HACKING
Normal file
@ -0,0 +1,331 @@
|
||||
Coding guide
|
||||
============
|
||||
|
||||
Our goal is to have all JavaScript code in GNOME follow a consistent style. In
|
||||
a dynamic language like JavaScript, it is essential to be rigorous about style
|
||||
(and unit tests), or you rapidly end up with a spaghetti-code mess.
|
||||
|
||||
A quick note
|
||||
------------
|
||||
|
||||
Life isn't fun if you can't break the rules. If a rule seems unnecessarily
|
||||
restrictive while you're coding, ignore it, and let the patch reviewer decide
|
||||
what to do.
|
||||
|
||||
Indentation and whitespace
|
||||
--------------------------
|
||||
|
||||
Use four-space indents. Braces are on the same line as their associated
|
||||
statements. You should only omit braces if *both* sides of the statement are
|
||||
on one line.
|
||||
|
||||
* One space after the `function` keyword. No space between the function name
|
||||
* in a declaration or a call. One space before the parens in the `if`
|
||||
* statements, or `while`, or `for` loops.
|
||||
|
||||
function foo(a, b) {
|
||||
let bar;
|
||||
|
||||
if (a > b)
|
||||
bar = do_thing(a);
|
||||
else
|
||||
bar = do_thing(b);
|
||||
|
||||
if (var == 5) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
print(i);
|
||||
}
|
||||
} else {
|
||||
print(20);
|
||||
}
|
||||
}
|
||||
|
||||
Semicolons
|
||||
----------
|
||||
|
||||
JavaScript allows omitting semicolons at the end of lines, but don't. Always
|
||||
end statements with a semicolon.
|
||||
|
||||
js2-mode
|
||||
--------
|
||||
|
||||
If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a
|
||||
while. emacs now has a built-in JavaScript mode, js-mode, based on
|
||||
espresso-mode. It is the de facto emacs mode for JavaScript.
|
||||
|
||||
File naming and creation
|
||||
------------------------
|
||||
|
||||
For JavaScript files, use lowerCamelCase-style names, with a `.js` extension.
|
||||
|
||||
We only use C where gjs/gobject-introspection is not available for the task, or
|
||||
where C would be cleaner. To work around limitations in
|
||||
gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`.
|
||||
|
||||
Like many other GNOME projects, we prefix our C source filenames with the
|
||||
library name followed by a dash, e.g. `shell-app-system.c`. Create a
|
||||
`-private.h` header when you want to share code internally in the
|
||||
library. These headers are not installed, distributed or introspected.
|
||||
|
||||
Imports
|
||||
-------
|
||||
|
||||
Use UpperCamelCase when importing modules to distinguish them from ordinary
|
||||
variables, e.g.
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
|
||||
Imports should be categorized into one of two places. The top-most import block
|
||||
should contain only "environment imports". These are either modules from
|
||||
gobject-introspection or modules added by gjs itself.
|
||||
|
||||
The second block of imports should contain only "application imports". These
|
||||
are the JS code that is in the gnome-shell codebase,
|
||||
e.g. `imports.ui.popupMenu`.
|
||||
|
||||
Each import block should be sorted alphabetically. Don't import modules you
|
||||
don't use.
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
The alphabetical ordering should be done independently of the location of the
|
||||
location. Never reference `imports` in actual code.
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
We use CONSTANTS_CASE to define constants. All constants should be directly
|
||||
under the imports:
|
||||
|
||||
const MY_DBUS_INTERFACE = 'org.my.Interface';
|
||||
|
||||
Variable declaration
|
||||
--------------------
|
||||
|
||||
Always use either `const` or `let` when defining a variable.
|
||||
|
||||
// Iterating over an array
|
||||
for (let i = 0; i < arr.length; ++i) {
|
||||
let item = arr[i];
|
||||
}
|
||||
|
||||
// Iterating over an object's properties
|
||||
for (let prop in someobj) {
|
||||
...
|
||||
}
|
||||
|
||||
If you use "var" then the variable is added to function scope, not block scope.
|
||||
See [What's new in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7#Block_scope_with_let_%28Merge_into_let_Statement%29)
|
||||
|
||||
Classes
|
||||
-------
|
||||
|
||||
There are many approaches to classes in JavaScript. We use our own class framework
|
||||
(sigh), which is built in gjs. The advantage is that it supports inheriting from
|
||||
GObjects, although this feature isn't used very often in the Shell itself.
|
||||
|
||||
const IconLabelMenuItem = new Lang.Class({
|
||||
Name: 'IconLabelMenuItem',
|
||||
Extends: PopupMenu.PopupMenuBaseItem,
|
||||
|
||||
_init: function(icon, label) {
|
||||
this.parent({ reactive: false });
|
||||
this.addActor(icon);
|
||||
this.addActor(label);
|
||||
},
|
||||
|
||||
open: function() {
|
||||
log("menu opened!");
|
||||
}
|
||||
});
|
||||
|
||||
* 'Name' is required. 'Extends' is optional. If you leave it out, you will
|
||||
automatically inherit from Object.
|
||||
|
||||
* Leave a blank line between the "class header" (Name, Extends, and other
|
||||
things) and the "class body" (methods). Leave a blank line between each
|
||||
method.
|
||||
|
||||
* No space before the colon, one space after.
|
||||
|
||||
* No trailing comma after the last item.
|
||||
|
||||
* Make sure to use a semicolon after the closing paren to the class. It's
|
||||
still a giant function call, even though it may resemble a more
|
||||
conventional syntax.
|
||||
|
||||
GObject Introspection
|
||||
---------------------
|
||||
|
||||
GObject Introspection is a powerful feature that allows us to have native
|
||||
bindings for almost any library built around GObject. If a library requires
|
||||
you to inherit from a type to use it, you can do so:
|
||||
|
||||
const MyClutterActor = new Lang.Class({
|
||||
Name: 'MyClutterActor',
|
||||
Extends: Clutter.Actor,
|
||||
|
||||
vfunc_get_preferred_width: function(actor, forHeight) {
|
||||
return [100, 100];
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(actor, forWidth) {
|
||||
return [100, 100];
|
||||
},
|
||||
|
||||
vfunc_paint: function(actor) {
|
||||
let alloc = this.get_allocation_box();
|
||||
Cogl.set_source_color4ub(255, 0, 0, 255);
|
||||
Cogl.rectangle(alloc.x1, alloc.y1,
|
||||
alloc.x2, alloc.y2);
|
||||
}
|
||||
});
|
||||
|
||||
Translatable strings, `environment.js`
|
||||
--------------------------------------
|
||||
|
||||
We use gettext to translate the GNOME Shell into all the languages that GNOME
|
||||
supports. The `gettext` function is aliased globally as `_`, you do not need to
|
||||
explicitly import it. This is done through some magic in the
|
||||
[environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js)
|
||||
file. If you can't find a method that's used, it's probably either in gjs itself
|
||||
or installed on the global object from the Environment.
|
||||
|
||||
Use 'single quotes' for programming strings that should not be translated
|
||||
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`
|
||||
-----------------------
|
||||
|
||||
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".
|
||||
|
||||
We sometimes use expando properties to set a property called `_delegate` on
|
||||
the actor itself:
|
||||
|
||||
const MyClass = new Lang.Class({
|
||||
Name: 'MyClass',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Button({ text: "This is a button" });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
},
|
||||
|
||||
_onClicked: function(actor) {
|
||||
actor.set_label("You clicked the button!");
|
||||
}
|
||||
});
|
||||
|
||||
The 'delegate' property is important for anything which trying to get the
|
||||
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.
|
||||
|
||||
Functional style
|
||||
----------------
|
||||
|
||||
JavaScript Array objects offer a lot of common functional programming
|
||||
capabilities such as forEach, map, filter and so on. You can use these when
|
||||
they make sense, but please don't have a spaghetti mess of function programming
|
||||
messed in a procedural style. Use your best judgment.
|
||||
|
||||
Closures
|
||||
--------
|
||||
|
||||
`this` will not be captured in a closure, it is relative to how the closure is
|
||||
invoked, not to the value of this where the closure is created, because "this"
|
||||
is a keyword with a value passed in at function invocation time, it is not a
|
||||
variable that can be captured in closures.
|
||||
|
||||
All closures should be wrapped with a Lang.bind.
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
let closure = Lang.bind(this, function() { this._fnorbate(); });
|
||||
|
||||
A more realistic example would be connecting to a signal on a method of a
|
||||
prototype:
|
||||
|
||||
const Lang = imports.lang;
|
||||
const FnorbLib = imports.fborbLib;
|
||||
|
||||
const MyClass = new Lang.Class({
|
||||
_init: function() {
|
||||
let fnorb = new FnorbLib.Fnorb();
|
||||
fnorb.connect('frobate', Lang.bind(this, this._onFnorbFrobate));
|
||||
},
|
||||
|
||||
_onFnorbFrobate: function(fnorb) {
|
||||
this._updateFnorb();
|
||||
}
|
||||
});
|
||||
|
||||
Object literal syntax
|
||||
---------------------
|
||||
|
||||
In JavaScript, these are equivalent:
|
||||
|
||||
foo = { 'bar': 42 };
|
||||
foo = { bar: 42 };
|
||||
|
||||
and so are these:
|
||||
|
||||
var b = foo['bar'];
|
||||
var b = foo.bar;
|
||||
|
||||
If your usage of an object is like an object, then you're defining "member
|
||||
variables." For member variables, use the no-quotes no-brackets syntax: `{ bar:
|
||||
42 }` `foo.bar`.
|
||||
|
||||
If your usage of an object is like a hash table (and thus conceptually the keys
|
||||
can have special chars in them), don't use quotes, but use brackets: `{ bar: 42
|
||||
}`, `foo['bar']`.
|
||||
|
||||
Getters, setters, and Tweener
|
||||
-----------------------------
|
||||
|
||||
Getters and setters should be used when you are dealing with an API that is
|
||||
designed around setting properties, like Tweener. If you want to animate an
|
||||
arbitrary property, create a getter and setter, and use Tweener to animate the
|
||||
property.
|
||||
|
||||
const ANIMATION_TIME = 2000;
|
||||
|
||||
const MyClass = new Lang.Class({
|
||||
Name: 'MyClass',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout();
|
||||
this._position = 0;
|
||||
},
|
||||
|
||||
get position() {
|
||||
return this._position;
|
||||
},
|
||||
|
||||
set position(value) {
|
||||
this._position = value;
|
||||
this.actor.set_position(value, value);
|
||||
}
|
||||
});
|
||||
|
||||
let myThing = new MyClass();
|
||||
Tweener.addTween(myThing,
|
||||
{ position: 100,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
@ -13,6 +13,7 @@ EXTRA_DIST = \
|
||||
DIST_EXCLUDE = \
|
||||
.gitignore \
|
||||
gnome-shell.doap \
|
||||
HACKING \
|
||||
MAINTAINERS \
|
||||
tools/build/*
|
||||
|
||||
|
184
NEWS
184
NEWS
@ -1,3 +1,187 @@
|
||||
3.7.1
|
||||
=====
|
||||
* Add shortcut to open application view directly [Jeremy; #685738]
|
||||
* Expose '<Super>F10' shortcut in System Settings [Florian; #672909]
|
||||
* Clean up timestamp format in chat notifications [Carlos; #680989]
|
||||
* loginScreen: Add support for 'disable-restart-buttons' [Florian; #686247]
|
||||
* Update textures automatically on file changes [Florian; #679268]
|
||||
* Implement org.gnome.ScreenSaver.GetActiveTime [Giovanni; #686064]
|
||||
* Add missing translations for GSetting schema [Giovanni; #686413]
|
||||
* Hide workspace switcher completely when it's not necessary [Seif; #686483]
|
||||
* Explicitly load gnome-screensaver when not running GDM [Tim; #683060]
|
||||
* Port to GnomeIdleMonitor [Jasper; #682224]
|
||||
* Set Empathy as preferred handler when delegating channels [Xavier; #686296]
|
||||
* Allow testing GDM login dialog from the session [Giovanni; #683725]
|
||||
* Use all available space for windows in window picker [Jasper, Pierre-Eric;
|
||||
#582650]
|
||||
* Use logind for suspend if available [Florian; #686482]
|
||||
* Misc. fixes and cleanups [Jasper, Florian, Adel, Rui; #677426, #680426,
|
||||
#686233, #686241, #686318, #686240, #686484, #686002, #684650, #686487]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Adel Gadllah, Seif Lotfy,
|
||||
Tim Lunn, Rui Matos, Florian Müllner, Pierre-Eric Pelloux-Prayer,
|
||||
Carlos Soriano, Jasper St. Pierre
|
||||
|
||||
Translations:
|
||||
Andika Triwidada [id], Matej Urbančič [sl], Ihar Hrachyshka [be],
|
||||
Daniel Mustieles [es], Fran Diéguez [gl], Takayuki KUSANO [ja],
|
||||
Мирослав Николић [sr, sr@latin], Dušan Kazik [sk], Tom Tryfonidis [el]
|
||||
|
||||
3.6.1
|
||||
=====
|
||||
* dash: Make padding even on the top/bottom of the dash [Jasper; #684619]
|
||||
* Fix a crash when dragging search results [Jasper; #684888]
|
||||
* workspaceThumbnail: Fix dragging with static workspaces [Jasper; #684641]
|
||||
* Really hide 'Show Keyboard Layout' on the lock screen [Matthias]
|
||||
* Misc. improvements to jhbuild setup [Owen; #685352, #685353, #685354, #685355]
|
||||
* Show message tray in Ctrl+Alt+Tab outside of the overview [Jasper, Florian;
|
||||
#684633, #685914]
|
||||
* Disable hotplug sniffer on remote filesystems [Jasper; #684093]
|
||||
* userMenu: Remove 'Switch Session' item [Florian; #685062]
|
||||
* unlockDialog: Make prompt entry insensitive while logging in [Jasper; #685444]
|
||||
* messageTray: Don't animate desktop clone for failed grabs [Jasper; #685342]
|
||||
* Fix crash on dragging windows between workspaces [Ryan; #681399]
|
||||
* userMenu: Ignore 'lock-enabled' setting for user switching [Florian; #685536]
|
||||
* gdm: Fix key-focus on first user [Adel; #684650]
|
||||
* Make grid button insensitive when dragging non-favorites [Jasper; #685313]
|
||||
* Calendar: hide all actions when on the login screen [Matthias; #685142]
|
||||
* Adapt unlock dialog layout for the login screen [Florian; #685201]
|
||||
* Make focus-follows-mouse work better with Shell UI [Florian; #678169]
|
||||
* Improve look of screen shield [Jasper; #685919]
|
||||
* Fix keynav in the login screen [Florian; #684730]
|
||||
* dateMenu: Hide "Open Calendar" item if calendar unavailable [Florian; #686050]
|
||||
* unlockDialog: Reset UI on verification failure [Giovanni; #685441]
|
||||
* Show unlock dialog on primary monitor when using keynav [Giovanni; #685855]
|
||||
* Fix height changes of entries when entering text [Florian; #685534]
|
||||
* Fix show-apps label after successful drags [Florian; #684627]
|
||||
* Misc. bugfixes and cleanups [Jasper, Olivier, Florian, Owen, Adel, Tanner, Tim, Matthias; #685434, #685511, #685466, #685341, #685156, #681159, #673189, #686016, 684869, #686079, #686063
|
||||
|
||||
Contributors:
|
||||
Jasper St. Pierre
|
||||
Matthias Clasen
|
||||
Owen Taylor
|
||||
Olivier Blin
|
||||
Florian Müllner
|
||||
Ryan Lortie
|
||||
Adel Gadllah
|
||||
Tanner Doshier
|
||||
Tim Lunn
|
||||
Giovanni Campagna
|
||||
|
||||
Translations:
|
||||
Tobias Endrigkeit [de], Rudolfs Mazurs [lv], Ask H. Larsen [da],
|
||||
Shankar Prasad [kn], Changwoo Ryu [ko], Chris Leonard [en_GB],
|
||||
Arash Mousavi [fa], Theppitak Karoonboonyanan [th], Seán de Búrca [ga],
|
||||
Yaron Shahrabani [he], Alexander Shopov [bg], Žygimantas Beručka [lt],
|
||||
Milo Casagrande [it], Kjartan Maraas [nb], Kris Thomsen [da],
|
||||
Aurimas Černius [lt], Yuri Myasoedov [ru], Мирослав Николић [sr],
|
||||
Marek Černocký [cs], Gabor Kelemen [hu], Ihar Hrachyshka [be],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW], Eleanor Chen [zh_CN],
|
||||
Carles Ferrando [ca@valencia], Vicent Cubells [ca], Daniel Korostil [uk],
|
||||
Alexandre Franke [fr], Piotr Drąg [pl]
|
||||
|
||||
3.6.0
|
||||
=====
|
||||
* keyboard: Make input source items accessible [Florian; #684462]
|
||||
* Don't show network dialogs in the lock screen [Giovanni; #684384]
|
||||
* popupMenu: Fix initial visibility of settings items [Florian; #684473]
|
||||
* userMenu: Close menu immediately on user/session switch [Florian; #684459]
|
||||
* Fix alignment of search section headers in RTL locales [Florian; #684379]
|
||||
* screenShield: Fix unlock animation [Florian; #684591]
|
||||
* Don't open the tray from a dwell while in a modal grab [Jasper; #684458]
|
||||
* userMenu: Fix texture updates on icon changes [Florian; #679268]
|
||||
* Fix a11y support in the login screen [Florian, Ray; #684727, #684728, #684748]
|
||||
* Make On-Screen-Keyboard usable with new message tray [Giovanni, Florian;
|
||||
#683546]
|
||||
* Fix initial visibility of input volume in lock-screen [Florian; #684611]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Florian Müllner, Jasper St. Pierre, Ray Strode
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Dr.T.Vasudevan [ta], Piotr Drąg [pl], A S Alam [pa],
|
||||
Alexander Shopov [bg], Nilamdyuti Goswami [as], Chandan Kumar [hi],
|
||||
Khaled Hosny [ar], Ibrahim Saed [ar], Sandeep Sheshrao Shedmake [mr],
|
||||
Tom Tryfonidis [el], Theppitak Karoonboonyanan [th], Alexandre Franke [fr],
|
||||
Fran Diéguez [gl], Gabor Kelemen [hu], Ani Peter [ml], Daniel Mustieles [es],
|
||||
Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], ManojKumar Giri [or],
|
||||
Ihar Hrachyshka [be], Aurimas Černius [lt], Djavan Fagundes [pt_BR],
|
||||
Changwoo Ryu [ko], Bruce Cowan [en_GB], Kris Thomsen [da], Gil Forcada [ca],
|
||||
Yaron Shahrabani [he], Milo Casagrande [it], Ville-Pekka Vainio [fi],
|
||||
YunQiang Su [zh_CN], Carles Ferrando [ca@valencia], Mario Blättermann [de],
|
||||
Rajesh Ranjan [hi], Yuri Myasoedov [ru], Rūdolfs Mazurs [lv],
|
||||
Jiro Matsuzawa [ja], Mattias Põldaru [et], Timur Zhamakeev [ky],
|
||||
Petr Kovar [cs], Chao-Hsiung Liao [zh_HK,zh_TW], Andika Triwidada [id]
|
||||
|
||||
3.5.92
|
||||
======
|
||||
* Login/UnlockDialog: Don't reset immediately if auth fails [Giovanni; #682544]
|
||||
* Allow changing session mode at runtime [Jasper, Giovanni; #683156]
|
||||
* Add zoom out animation on login [Jasper; #683170]
|
||||
* Bluetooth: don't restrict the length of non numeric PINs [Giovanni; #683356]
|
||||
* Force chat notification to stay open when focusing entry [Debarshi; #682236]
|
||||
* Make sure the screen is fully locked before suspending [Giovanni; #683448]
|
||||
* st-texture-cache: Fix a case of distorted textures [Florian; #683483]
|
||||
* popupSubMenu: Fix padding for non-scrolled submenus [Florian; #683009]
|
||||
* popupMenu: Fix width changes on submenu open/close [Florian; #683485]
|
||||
* boxpointer: Avoid malformed boxpointer arrow [Debarshi; #680077]
|
||||
* Change stage background color to grey [Adel; #683514]
|
||||
* messageTray: Update style of summary counters [Debarshi; #682891]
|
||||
* Don't fail if a legacy tray icon has no WM_CLASS [Giovanni; #683724]
|
||||
* PolkitAgent: Fix a crash if there is no avatar [Giovanni; #683707]
|
||||
* Hide the a11y menu in the lock screen, but show it in the login screen
|
||||
[Giovanni; #682542]
|
||||
* Fix show-apps button dropping off the dash [Florian; #683340]
|
||||
* Fix committing strings to shell entries from input method [Florian; #658325]
|
||||
* Make IBus display strings consistent with control-center [Rui; #683124]
|
||||
* Fix missing short codes for some input sources [Rui; #683613]
|
||||
* Remove support for long-press from entry context menus [Jasper; #683509]
|
||||
* screenShield: Add box-shadow to the shield [Florian]
|
||||
* Don't show a right-click menu for the hotplug source [Jasper; #683438]
|
||||
* Fix extension styling [Giovanni; #682128]
|
||||
* Fix on-screen keyboard not working with system-modal dialogs
|
||||
[Giovanni; #664309]
|
||||
* Fix insensitive styling for popup menu items [Giovanni; #683988]
|
||||
* Disable the message tray dwell when the user is interacting [Owen; #683811]
|
||||
* Animate going from the unlock dialog to the lock screen [Giovanni; #681143]
|
||||
* Autostart fprintd when necessary [Ray; #683131]
|
||||
* UnlockDialog: Allow typing before the first PAM question [Giovanni; #681576]
|
||||
* Make Return key dismiss screenshield [Ray; #683889]
|
||||
* Fix keyboard navigation in the message tray [Florian; #682243]
|
||||
* Remove the places & devices search provider [Giovanni; #683506]
|
||||
* Enable hot corner while the message tray is up [Florian; #682255]
|
||||
* Port screen recorder to new GStreamer vp8enc API [Adel; #684206]
|
||||
* Fix fish flickering [Giovanni; #684154]
|
||||
* Fix extension ordering with !important [Jasper; #684163]
|
||||
* Allow the shell to run without the screenshield [Giovanni; #683060]
|
||||
* Add menu items for IBus Anthy's InputMode, TypingMode [Rui; #682314]
|
||||
* Improve transition to the login dialog [Jasper; #682428]
|
||||
* Keep unlock dialog around until shield animation ends [Florian; #684342]
|
||||
* Expose shell keybindings in System Settings [Florian; #671010]
|
||||
* Misc. bugfixes and cleanups [Debarshi, Florian, Giovanni, Jasper, Rico, Rui;
|
||||
#672790, #677434, #683305, #683357, #683369, #683377, #683378, #683400,
|
||||
#683449, #683472, #683482, #683487, #683488, #683526, #683529, #683546,
|
||||
#683583, #683628, #683705, #683982, #683989, #684035, #684036, #684040,
|
||||
#684162, #684214, #684343]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray,
|
||||
Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Gabor Kelemen [hu], Piotr Drąg [pl], Khaled Hosny [ar],
|
||||
Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||
Bruce Cowan [en_GB], Dirgita [id], Tom Tryfonidis [el], Timo Jyrinki [fi],
|
||||
Adorilson Bezerra [pt_BR], Arash Mousavi [fa], Matej Urbančič [sl],
|
||||
Christian Kirbach [de], Yaron Shahrabani [he], Ihar Hrachyshka [be],
|
||||
Changwoo Ryu [ko], Duarte Loreto [pt], Theppitak Karoonboonyanan [th],
|
||||
Nilamdyuti Goswami [as], Sandeep Sheshrao Shedmake [mr],
|
||||
Alexandre Franke [fr], Ivaylo Valkov [bg], tuhaihe [zh_CN],
|
||||
Yuri Myasoedov [ru], Aurimas Černius [lt], Andika Triwidada [id],
|
||||
Rajesh Ranjan [hi], Sweta Kothari [gu], Daniel Mustieles [es],
|
||||
Fran Diéguez [gl], Praveen Illa [te]
|
||||
|
||||
3.5.91
|
||||
======
|
||||
* Improve modal dialog styling of network secret prompts [Jasper; #682412]
|
||||
|
@ -153,8 +153,6 @@ NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
|
||||
/* global initialization routine, called once when plugin
|
||||
is loaded */
|
||||
|
||||
g_type_init ();
|
||||
|
||||
g_debug ("plugin loaded");
|
||||
|
||||
memcpy (&funcs, pfuncs, sizeof (funcs));
|
||||
|
20
configure.ac
20
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.5.91],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.7.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -52,7 +52,7 @@ AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
|
||||
if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11"
|
||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
@ -63,9 +63,9 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
CLUTTER_MIN_VERSION=1.11.11
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.33.2
|
||||
MUTTER_MIN_VERSION=3.5.91
|
||||
MUTTER_MIN_VERSION=3.7.1
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.31.6
|
||||
GIO_MIN_VERSION=2.35.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
|
||||
@ -74,7 +74,7 @@ TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.3.90
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
|
||||
# Collect more than 20 libraries for a prize!
|
||||
@ -97,8 +97,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
libnm-glib libnm-util gnome-keyring-1
|
||||
gcr-3 >= $GCR_MIN_VERSION
|
||||
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION)
|
||||
gcr-3 >= $GCR_MIN_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
@ -106,6 +105,9 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||
|
||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
@ -117,7 +119,7 @@ CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 x11)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.4)
|
||||
@ -136,7 +138,7 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||
AC_SUBST([HAVE_BLUETOOTH],[0])
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
|
||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
|
||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||
|
||||
|
12
data/50-gnome-shell-screenshot.xml.in
Normal file
12
data/50-gnome-shell-screenshot.xml.in
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||
group="system"
|
||||
_name="Screenshots"
|
||||
wm_name="GNOME Shell"
|
||||
package="gnome-shell">
|
||||
|
||||
<KeyListEntry name="toggle-recording"
|
||||
_description="Record a screencast"/>
|
||||
|
||||
</KeyListEntries>
|
||||
|
18
data/50-gnome-shell-system.xml.in
Normal file
18
data/50-gnome-shell-system.xml.in
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<KeyListEntries schema="org.gnome.shell.keybindings"
|
||||
group="system"
|
||||
_name="System"
|
||||
wm_name="GNOME Shell"
|
||||
package="gnome-shell">
|
||||
|
||||
<KeyListEntry name="toggle-message-tray"
|
||||
_description="Show the message tray"/>
|
||||
|
||||
<KeyListEntry name="toggle-application-view"
|
||||
_description="Show all applications"/>
|
||||
|
||||
<KeyListEntry name="open-application-menu"
|
||||
_description="Open the application menu"/>
|
||||
|
||||
</KeyListEntries>
|
||||
|
@ -39,6 +39,7 @@ dist_theme_DATA = \
|
||||
theme/process-working.svg \
|
||||
theme/running-indicator.svg \
|
||||
theme/source-button-border.svg \
|
||||
theme/summary-counter.svg \
|
||||
theme/toggle-off-us.svg \
|
||||
theme/toggle-off-intl.svg \
|
||||
theme/toggle-on-us.svg \
|
||||
@ -46,6 +47,13 @@ dist_theme_DATA = \
|
||||
theme/ws-switch-arrow-up.png \
|
||||
theme/ws-switch-arrow-down.png
|
||||
|
||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||
keys_in_files = \
|
||||
50-gnome-shell-screenshot.xml.in \
|
||||
50-gnome-shell-system.xml.in \
|
||||
$(NULL)
|
||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||
|
||||
@INTLTOOL_XML_NOMERGE_RULE@
|
||||
@ -72,12 +80,14 @@ EXTRA_DIST = \
|
||||
$(introspection_DATA) \
|
||||
$(menu_DATA) \
|
||||
$(convert_DATA) \
|
||||
$(keys_in_files) \
|
||||
org.gnome.shell.gschema.xml.in.in
|
||||
|
||||
CLEANFILES = \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-extension-prefs.in \
|
||||
$(desktop_DATA) \
|
||||
$(keys_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
gschemas.compiled \
|
||||
org.gnome.shell.gschema.valid \
|
||||
|
@ -108,14 +108,18 @@
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
<arg type="a{sv}" direction="out">
|
||||
<arg type="aa{sv}" direction="out">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
A dictionary describing the given search result, containing
|
||||
'id', 'name' (both strings) and either 'icon' (a serialized
|
||||
GIcon) or 'icon-data' (raw image data as (iiibiiay) - width,
|
||||
height, rowstride, has-alpha, bits per sample, channels, data)
|
||||
'id' and 'name' (both strings). Optionally, either 'gicon' (a
|
||||
serialized GIcon) or 'icon-data' (raw image data as (iiibiiay)
|
||||
- width, height, rowstride, has-alpha, bits per sample,
|
||||
channels, data) can be specified if the result can be better
|
||||
served with a thumbnail of the content (such as with images).
|
||||
A 'description' field (string) may also be specified if more
|
||||
context would help the user find the desired result.
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
@ -143,5 +147,25 @@
|
||||
</doc:doc>
|
||||
</arg>
|
||||
</method>
|
||||
|
||||
<method name="LaunchSearch">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
Called when the user clicks on the provider icon. The provider
|
||||
application should open and run the active search term itself.
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
<arg type="as" direction="in">
|
||||
<doc:doc>
|
||||
<doc:summary>
|
||||
<doc:para>
|
||||
The current search term(s).
|
||||
</doc:para>
|
||||
</doc:summary>
|
||||
</doc:doc>
|
||||
</arg>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
|
@ -39,10 +39,6 @@
|
||||
will be displayed in the favorites area.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="disabled-open-search-providers" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>disabled OpenSearch providers</_summary>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||
@ -61,6 +57,14 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
|
||||
<_summary>Internally used to store the last session presence status for the user. The
|
||||
value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
<key name="always-show-log-out" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
|
||||
<_description>
|
||||
This key overrides the automatic hiding of the 'Log out'
|
||||
menuitem in single-user, single-session situations.
|
||||
</_description>
|
||||
</key>
|
||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||
@ -87,6 +91,14 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
Keybinding to open the application menu.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="toggle-application-view" type="as">
|
||||
<default>["<Super>a"]</default>
|
||||
<_summary>Keybinding to open the "Show Applications" view</_summary>
|
||||
<_description>
|
||||
Keybinding to open the "Show Applications" view of the Activities
|
||||
Overview.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="toggle-message-tray" type="as">
|
||||
<default>["<Super>m"]</default>
|
||||
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
|
||||
@ -136,7 +148,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
take care of its own output - this might be used to send the output
|
||||
to an icecast server via shout2send or similar. When unset or set
|
||||
to an empty value, the default pipeline will be used. This is currently
|
||||
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
|
||||
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
|
||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||
for a guess at the optimal thread count on the system.
|
||||
</_description>
|
||||
@ -152,44 +164,53 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/">
|
||||
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="attach-modal-dialogs" type="b">
|
||||
<default>true</default>
|
||||
<summary>Attach modal dialog to the parent window</summary>
|
||||
<description>
|
||||
<_summary>Attach modal dialog to the parent window</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.mutter when running
|
||||
GNOME Shell.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="button-layout" type="s">
|
||||
<default>":close"</default>
|
||||
<summary>Arrangement of buttons on the titlebar</summary>
|
||||
<description>
|
||||
<_summary>Arrangement of buttons on the titlebar</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.desktop.wm.preferences when
|
||||
running GNOME Shell.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="edge-tiling" type="b">
|
||||
<default>true</default>
|
||||
<summary>Enable edge tiling when dropping windows on screen edges</summary>
|
||||
<description>
|
||||
<_summary>Enable edge tiling when dropping windows on screen edges</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="dynamic-workspaces" type="b">
|
||||
<default>true</default>
|
||||
<summary>Workspaces are managed dynamically</summary>
|
||||
<description>
|
||||
<_summary>Workspaces are managed dynamically</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="workspaces-only-on-primary" type="b">
|
||||
<default>true</default>
|
||||
<summary>Workspaces only on primary monitor</summary>
|
||||
<_summary>Workspaces only on primary monitor</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="focus-change-on-pointer-rest" type="b">
|
||||
<default>true</default>
|
||||
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
||||
<description>
|
||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||
</description>
|
||||
|
@ -39,7 +39,6 @@ stage {
|
||||
/* small */
|
||||
.app-well-menu,
|
||||
.contact-details-status,
|
||||
.run-dialog-label,
|
||||
.run-dialog-error-label {
|
||||
font-size: 9pt;
|
||||
}
|
||||
@ -151,11 +150,11 @@ StScrollBar StButton#vhandle:active {
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
||||
}
|
||||
|
||||
.popup-sub-menu .popup-menu-item:ltr {
|
||||
.popup-sub-menu:scrolled .popup-menu-item:ltr {
|
||||
padding-right: 0em;
|
||||
}
|
||||
|
||||
.popup-sub-menu .popup-menu-item:rtl {
|
||||
.popup-sub-menu:scrolled .popup-menu-item:rtl {
|
||||
padding-left: 0em;
|
||||
}
|
||||
|
||||
@ -195,7 +194,7 @@ StScrollBar StButton#vhandle:active {
|
||||
background-color: #4c4c4c;
|
||||
}
|
||||
|
||||
StButton.popup-menu-item:insensitive {
|
||||
.popup-menu-item:insensitive {
|
||||
color: #9f9f9f;
|
||||
}
|
||||
|
||||
@ -234,13 +233,18 @@ StButton.popup-menu-item:insensitive {
|
||||
spacing: .5em;
|
||||
}
|
||||
|
||||
.popup-inactive-menu-item {
|
||||
.popup-status-menu-item {
|
||||
font-weight: normal;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.popup-subtitle-menu-item {
|
||||
.popup-inactive-menu-item, .popup-inactive-menu-item:insensitive {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.popup-menu-icon {
|
||||
@ -306,7 +310,7 @@ StButton.popup-menu-item:insensitive {
|
||||
.notification-icon-button:focus,
|
||||
.hotplug-notification-item:focus,
|
||||
.modal-dialog-button:focus {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.dash-search-button:active,
|
||||
@ -321,26 +325,30 @@ StButton.popup-menu-item:insensitive {
|
||||
background-gradient-end: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.notification-button:insensitive,
|
||||
.notification-icon-button:insensitive,
|
||||
.notification-button:insensitive {
|
||||
.modal-dialog-button:insensitive {
|
||||
border-color: #666666;
|
||||
color: #9f9f9f;
|
||||
background-gradient-direction: none;
|
||||
background-color: rgba(102, 102, 102, 0.15);
|
||||
}
|
||||
|
||||
/* Entries */
|
||||
|
||||
#searchEntry,
|
||||
.notification StEntry,
|
||||
.login-dialog-prompt-entry,
|
||||
.prompt-dialog-password-entry {
|
||||
.modal-dialog StEntry {
|
||||
color: rgb(64, 64, 64);
|
||||
caret-color: rgb(64, 64, 64);
|
||||
font-size: 12pt;
|
||||
caret-size: 1px;
|
||||
selected-color: black;
|
||||
selected-color: white;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
#searchEntry,
|
||||
.run-dialog-entry,
|
||||
.notification StEntry {
|
||||
border: 2px solid rgba(245,245,245,0.2);
|
||||
background-gradient-start: rgba(5,5,6,0.1);
|
||||
@ -353,8 +361,7 @@ StButton.popup-menu-item:insensitive {
|
||||
#searchEntry:focus,
|
||||
#searchEntry:hover,
|
||||
.notification StEntry:focus,
|
||||
.login-dialog-prompt-entry,
|
||||
.prompt-dialog-password-entry {
|
||||
.modal-dialog StEntry {
|
||||
border: 2px solid rgb(136,138,133);
|
||||
background-gradient-start: rgb(200,200,200);
|
||||
background-gradient-end: white;
|
||||
@ -363,12 +370,17 @@ StButton.popup-menu-item:insensitive {
|
||||
}
|
||||
|
||||
.notification StEntry:focus,
|
||||
.prompt-dialog-password-entry:focus,
|
||||
.login-dialog-prompt-entry:focus {
|
||||
.modal-dialog StEntry:focus {
|
||||
border: 2px solid #3465a4;
|
||||
}
|
||||
|
||||
#searchEntry {
|
||||
border-color: rgba(245,245,245,0.3);
|
||||
color: rgb(192, 192, 192);
|
||||
caret-color: rgb(192, 192, 192);
|
||||
}
|
||||
|
||||
#searchEntry:hover {
|
||||
color: rgb(128, 128, 128);
|
||||
caret-color: rgb(128, 128, 128);
|
||||
}
|
||||
@ -381,8 +393,7 @@ StButton.popup-menu-item:insensitive {
|
||||
}
|
||||
|
||||
.notification StEntry,
|
||||
.prompt-dialog-password-entry,
|
||||
.login-dialog-prompt-entry {
|
||||
.modal-dialog StEntry {
|
||||
border-radius: 5px;
|
||||
padding: 4px 4px;
|
||||
}
|
||||
@ -397,6 +408,8 @@ StButton.popup-menu-item:insensitive {
|
||||
.login-dialog-prompt-entry:insensitive {
|
||||
color: rgba(0,0,0,0.7);
|
||||
border: 2px solid #565656;
|
||||
background-gradient-start: rgb(200,200,200);
|
||||
background-gradient-end: rgb(210,210,210);
|
||||
}
|
||||
|
||||
/* Panel */
|
||||
@ -544,7 +557,6 @@ StButton.popup-menu-item:insensitive {
|
||||
}
|
||||
|
||||
.status-chooser-combo.popup-combo-menu {
|
||||
background-color: rgba(0,0,0,0.7);
|
||||
padding: .4em 0em;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #5f5f5f;
|
||||
@ -562,6 +574,10 @@ StButton.popup-menu-item:insensitive {
|
||||
/* Overview */
|
||||
|
||||
#overview {
|
||||
spacing: 40px;
|
||||
}
|
||||
|
||||
#overview-group {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
@ -607,9 +623,9 @@ StButton.popup-menu-item:insensitive {
|
||||
|
||||
.window-close, .notification-close {
|
||||
background-image: url("close-window.svg");
|
||||
background-size: 34px;
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
background-size: 32px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.window-close {
|
||||
@ -620,19 +636,31 @@ StButton.popup-menu-item:insensitive {
|
||||
/* we start out in the top right of the
|
||||
* notification, inset.
|
||||
*
|
||||
* center is 32px/2 = 17px
|
||||
* center is 32px/2 = 16px
|
||||
*
|
||||
* adjust left 2px
|
||||
* adjust down 8px */
|
||||
|
||||
-shell-close-overlap-x: 15px;
|
||||
-shell-close-overlap-y: 12px;
|
||||
-shell-close-overlap-x: 14px;
|
||||
-shell-close-overlap-y: -12px;
|
||||
}
|
||||
|
||||
.notification-close:rtl {
|
||||
/* as above, but starting out in the top left of the
|
||||
* notification. */
|
||||
|
||||
-shell-close-overlap-x: -14px;
|
||||
}
|
||||
|
||||
.window-close:rtl {
|
||||
-st-background-image-shadow: 2px 2px 6px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.window-picker {
|
||||
-horizontal-spacing: 40px;
|
||||
-vertical-spacing: 40px;
|
||||
}
|
||||
|
||||
/* Dash */
|
||||
|
||||
#dash {
|
||||
@ -651,11 +679,6 @@ StButton.popup-menu-item:insensitive {
|
||||
border-radius: 9px 0px 0px 9px;
|
||||
}
|
||||
|
||||
#dash:empty {
|
||||
height: 100px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
background-image: url("dash-placeholder.svg");
|
||||
background-size: contain;
|
||||
@ -675,6 +698,11 @@ StButton.popup-menu-item:insensitive {
|
||||
|
||||
.search-entry-icon {
|
||||
icon-size: 1em;
|
||||
color: #c0c0c0;
|
||||
}
|
||||
|
||||
#searchEntry:hover .search-entry-icon,
|
||||
#searchEntry:focus .search-entry-icon {
|
||||
color: #8d8f8a;
|
||||
}
|
||||
|
||||
@ -687,7 +715,7 @@ StButton.popup-menu-item:insensitive {
|
||||
|
||||
#searchResultsContent {
|
||||
padding-right: 20px;
|
||||
spacing: 36px;
|
||||
spacing: 16px;
|
||||
}
|
||||
|
||||
#searchResultsContent:rtl {
|
||||
@ -695,6 +723,25 @@ StButton.popup-menu-item:insensitive {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
/* This should be equal to #searchResultsContent spacing */
|
||||
spacing: 16px;
|
||||
}
|
||||
|
||||
.search-section-separator {
|
||||
-gradient-height: 1px;
|
||||
-gradient-start: rgba(255,255,255,0);
|
||||
-gradient-end: rgba(255,255,255,0.5);
|
||||
-margin-horizontal: 1.5em;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.search-section-content {
|
||||
/* This is the space between the provider icon and the results container */
|
||||
spacing: 25px;
|
||||
}
|
||||
|
||||
.search-statustext,
|
||||
.search-section-header {
|
||||
padding: 4px 12px;
|
||||
spacing: 4px;
|
||||
@ -724,6 +771,10 @@ StButton.popup-menu-item:insensitive {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
spacing: 5px;
|
||||
}
|
||||
|
||||
/* Text labels are an odd number of pixels tall. The uneven top and bottom
|
||||
* padding compensates for this and ensures that the label is vertically
|
||||
* centered */
|
||||
@ -804,7 +855,10 @@ StButton.popup-menu-item:insensitive {
|
||||
|
||||
.app-well-app > .overview-icon,
|
||||
.show-apps > .overview-icon,
|
||||
.search-result-content > .overview-icon {
|
||||
.remove-favorite > .overview-icon,
|
||||
.search-section-icon-bin,
|
||||
.search-result,
|
||||
.grid-search-result-content > .overview-icon {
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
border: 1px rgba(0,0,0,0);
|
||||
@ -820,7 +874,10 @@ StButton.popup-menu-item:insensitive {
|
||||
|
||||
.app-well-app:hover > .overview-icon,
|
||||
.show-apps:hover > .overview-icon,
|
||||
.search-result-content:hover > .overview-icon {
|
||||
.remove-favorite:hover > .overview-icon,
|
||||
.search-section-icon-bin:hover,
|
||||
.search-result:hover,
|
||||
.grid-search-result-content:hover > .overview-icon {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
text-shadow: black 0px 2px 2px;
|
||||
transition-duration: 100;
|
||||
@ -855,13 +912,39 @@ StButton.popup-menu-item:insensitive {
|
||||
}
|
||||
|
||||
.app-well-app:focus > .overview-icon,
|
||||
.search-result-content:focus > .overview-icon,
|
||||
.show-apps:focus > .overview-icon,
|
||||
.search-section-icon-bin:focus,
|
||||
.search-result:focus,
|
||||
.grid-search-result-content:focus > .overview-icon,
|
||||
.app-well-app:selected > .overview-icon,
|
||||
.search-result-content:selected > .overview-icon {
|
||||
.search-section-icon-bin:selected,
|
||||
.search-result:selected,
|
||||
.grid-search-result-content:selected > .overview-icon {
|
||||
background-color: rgba(255,255,255,0.33);
|
||||
}
|
||||
|
||||
/* List Results */
|
||||
|
||||
.search-result {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.search-result-content {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
.search-result-details {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.search-result-details-title {
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
.search-result-details-description {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
/* LookingGlass */
|
||||
|
||||
#LookingGlassDialog {
|
||||
@ -1190,12 +1273,16 @@ StButton.popup-menu-item:insensitive {
|
||||
background: #2e3436 url(message-tray-background.png);
|
||||
background-repeat: repeat;
|
||||
transition-duration: 250;
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
#message-tray:keyboard {
|
||||
/* Same as the OSK */
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
#message-tray:overview {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-top: 1px solid rgba(128, 128, 128, 0.3);
|
||||
outline: 1px solid rgba(128, 128, 128, 0.3);
|
||||
}
|
||||
|
||||
.notification {
|
||||
@ -1417,38 +1504,45 @@ StButton.popup-menu-item:insensitive {
|
||||
}
|
||||
|
||||
#summary-mode {
|
||||
padding: 0px 6px 0px 6px; /* same as the values in .summary-source */
|
||||
height: 60px;
|
||||
spacing: 10px;
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.summary-source-button {
|
||||
border-radius: 4px;
|
||||
transition-duration: 100;
|
||||
padding: 6px 3px 6px 3px;
|
||||
}
|
||||
|
||||
.summary-source-button:hover {
|
||||
.summary-source-button:last-child:ltr {
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.summary-source-button:last-child:rtl {
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.summary-source-button:hover .summary-source {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.summary-source-button:focus,
|
||||
.summary-source-button:selected {
|
||||
.summary-source-button:focus .summary-source,
|
||||
.summary-source-button:selected .summary-source {
|
||||
background-color: rgba(255,255,255,0.33);
|
||||
}
|
||||
|
||||
.summary-source {
|
||||
padding-right: 6px;
|
||||
padding-left: 6px;
|
||||
border-radius: 4px;
|
||||
padding: 0 6px 0 6px;
|
||||
transition-duration: 100;
|
||||
}
|
||||
|
||||
.summary-source-counter {
|
||||
color: white;
|
||||
background-color: #3465A4;
|
||||
text-shadow: black 1px 1px 0;
|
||||
font-size: 9pt;
|
||||
border-radius: 1em;
|
||||
min-height: 1em;
|
||||
min-width: 1em;
|
||||
background-image: url("summary-counter.svg");
|
||||
background-size: 2.5em;
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
height: 2.5em;
|
||||
width: 2.5em;
|
||||
-shell-counter-overlap-x: 4px;
|
||||
-shell-counter-overlap-y: 4px;
|
||||
}
|
||||
|
||||
/* App Switcher */
|
||||
@ -1615,35 +1709,26 @@ StButton.popup-menu-item:insensitive {
|
||||
padding: 4px 32px 5px;
|
||||
}
|
||||
|
||||
.modal-dialog-button:insensitive {
|
||||
color: rgb(60, 60, 60);
|
||||
}
|
||||
|
||||
.modal-dialog-button:focus {
|
||||
padding: 3px 31px 4px;
|
||||
}
|
||||
|
||||
/* Run Dialog */
|
||||
|
||||
.run-dialog-label {
|
||||
font-size: 12pt;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
padding-bottom: .4em;
|
||||
}
|
||||
|
||||
.run-dialog-error-box {
|
||||
padding-top: 15px;
|
||||
spacing: 5px;
|
||||
}
|
||||
|
||||
.run-dialog-entry {
|
||||
font-weight: bold;
|
||||
width: 23em;
|
||||
selection-background-color: white;
|
||||
selected-color: black;
|
||||
}
|
||||
|
||||
.run-dialog {
|
||||
border-radius: 16px;
|
||||
|
||||
padding-right: 21px;
|
||||
padding-left: 21px;
|
||||
padding-bottom: 15px;
|
||||
padding-top: 15px;
|
||||
.modal-dialog .run-dialog-entry {
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.lightbox {
|
||||
@ -2024,7 +2109,6 @@ StButton.popup-menu-item:insensitive {
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item {
|
||||
color: #666666;
|
||||
border-radius: 10px;
|
||||
padding: .2em;
|
||||
}
|
||||
@ -2042,6 +2126,11 @@ StButton.popup-menu-item:insensitive {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item,
|
||||
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
|
||||
@ -2089,6 +2178,7 @@ StButton.popup-menu-item:insensitive {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-not-listed-button:focus .login-dialog-not-listed-label,
|
||||
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
|
||||
color: #E8E8E8;
|
||||
}
|
||||
@ -2164,18 +2254,24 @@ StButton.popup-menu-item:insensitive {
|
||||
height: .75em;
|
||||
}
|
||||
|
||||
|
||||
.login-dialog .modal-dialog-button {
|
||||
border: 1px solid #666666;
|
||||
border-radius: 5px;
|
||||
padding: 3px 18px;
|
||||
}
|
||||
|
||||
.login-dialog .modal-dialog-button:focus {
|
||||
padding: 2px 17px;
|
||||
}
|
||||
|
||||
.login-dialog .modal-dialog-button:default {
|
||||
background-gradient-start: #6793c4;
|
||||
background-gradient-end: #335d8f;
|
||||
background-gradient-direction: vertical;
|
||||
border: 2px solid #16335d;
|
||||
border-color: #16335d;
|
||||
}
|
||||
|
||||
.login-dialog .modal-dialog-button:default:focus {
|
||||
border: 2px solid #377fe7;
|
||||
}
|
||||
|
||||
.login-dialog .modal-dialog-button:default:hover {
|
||||
@ -2189,12 +2285,28 @@ StButton.popup-menu-item:insensitive {
|
||||
background-gradient-end: #74a0d0;
|
||||
}
|
||||
|
||||
.login-dialog .modal-dialog-button:default:insensitive {
|
||||
border-color: #666666;
|
||||
color: #9f9f9f;
|
||||
background-gradient-direction: none;
|
||||
background-color: rgba(102, 102, 102, 0.15);
|
||||
}
|
||||
|
||||
.login-dialog-message-warning {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.unlock-dialog-user-name-container {
|
||||
spacing: .4em;
|
||||
}
|
||||
|
||||
/* Screen shield */
|
||||
|
||||
.screen-shield-background {
|
||||
background: black;
|
||||
box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
|
||||
}
|
||||
|
||||
#lockDialogGroup {
|
||||
background: #2e3436 url(noise-texture.png);
|
||||
background-repeat: repeat;
|
||||
@ -2212,6 +2324,10 @@ StButton.popup-menu-item:insensitive {
|
||||
-arrow-shadow: 0 1px 1px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.screen-shield-contents-box {
|
||||
spacing: 48px;
|
||||
}
|
||||
|
||||
.screen-shield-clock {
|
||||
color: white;
|
||||
text-shadow: 0px 1px 2px rgba(0,0,0,0.6);
|
||||
@ -2221,12 +2337,12 @@ StButton.popup-menu-item:insensitive {
|
||||
}
|
||||
|
||||
.screen-shield-clock-time {
|
||||
font-size: 86px;
|
||||
font-size: 72pt;
|
||||
text-shadow: 0px 2px 2px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.screen-shield-clock-date {
|
||||
font-size: 48px;
|
||||
font-size: 28pt;
|
||||
}
|
||||
|
||||
#screenShieldNotifications {
|
||||
@ -2234,11 +2350,12 @@ StButton.popup-menu-item:insensitive {
|
||||
background-color: rgba(0.0, 0.0, 0.0, 0.9);
|
||||
border: 2px solid #868686;
|
||||
max-height: 500px;
|
||||
padding: 12px 0;
|
||||
padding: 18px 0;
|
||||
box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.screen-shield-notifications-box {
|
||||
spacing: 12px;
|
||||
spacing: 18px;
|
||||
}
|
||||
|
||||
.screen-shield-notification-source {
|
||||
@ -2249,6 +2366,7 @@ StButton.popup-menu-item:insensitive {
|
||||
.screen-shield-notification-label {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
color: #babdb6;
|
||||
}
|
||||
|
||||
/* Remove background from notifications, otherwise
|
||||
|
120
data/theme/summary-counter.svg
Normal file
120
data/theme/summary-counter.svg
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
id="Foreground"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 23.272727 23.272727"
|
||||
enable-background="new 0 0 16 16"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
sodipodi:docname="summary-counter.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||
id="metadata2399"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2397"><linearGradient
|
||||
id="linearGradient3173"><stop
|
||||
style="stop-color:#c4c4c4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3175" /><stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3177" /></linearGradient><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 8 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="16 : 8 : 1"
|
||||
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
||||
id="perspective2401" /><filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter16494-4"
|
||||
x="-0.20989846"
|
||||
width="1.4197969"
|
||||
y="-0.20903821"
|
||||
height="1.4180764"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.3282637"
|
||||
id="feGaussianBlur16496-8" /></filter><radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient16498-6"
|
||||
id="radialGradient16504-1"
|
||||
cx="7.6582627"
|
||||
cy="5.8191104"
|
||||
fx="7.6582627"
|
||||
fy="5.8191104"
|
||||
r="8.6928644"
|
||||
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient16498-6"><stop
|
||||
style="stop-color:#9FD0FF;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop16500-8" /><stop
|
||||
style="stop-color:#3465A4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop16502-0" /></linearGradient></defs><sodipodi:namedview
|
||||
inkscape:window-height="709"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#000000"
|
||||
id="base"
|
||||
showgrid="false"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="15.386407"
|
||||
inkscape:cy="13.739577"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1179"
|
||||
inkscape:current-layer="g16402-8"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-maximized="1"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11246"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
|
||||
style="display:inline"
|
||||
id="g16402-8"
|
||||
transform="translate(4.7533483,2.8238929)"><g
|
||||
id="g3175-4"
|
||||
transform="translate(-0.89995416,0.94028614)"><path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="0"
|
||||
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
|
||||
xlink:href="#path2394-32"
|
||||
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
|
||||
id="path16480-5"
|
||||
inkscape:href="#path2394-32"
|
||||
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
|
||||
transform="translate(0,1.028519)" /><path
|
||||
clip-rule="evenodd"
|
||||
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
|
||||
id="path2394-32"
|
||||
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:nodetypes="csssc"
|
||||
inkscape:connector-curvature="0" /><g
|
||||
id="g3172-6" /></g></g></svg>
|
After Width: | Height: | Size: 5.4 KiB |
@ -29,8 +29,6 @@
|
||||
<chapter>
|
||||
<title>Search</title>
|
||||
<xi:include href="xml/shell-app-system.xml"/>
|
||||
<xi:include href="xml/shell-contact-system.xml"/>
|
||||
<xi:include href="xml/shell-doc-system.xml"/>
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Tray Icons</title>
|
||||
@ -42,7 +40,6 @@
|
||||
<chapter>
|
||||
<title>Recorder</title>
|
||||
<xi:include href="xml/shell-recorder.xml"/>
|
||||
<xi:include href="xml/shell-recorder-src.xml"/>
|
||||
</chapter>
|
||||
<chapter>
|
||||
<title>Integration helpers and utilities</title>
|
||||
|
@ -1,3 +1,4 @@
|
||||
NULL =
|
||||
|
||||
EXTRA_DIST = misc/config.js.in
|
||||
CLEANFILES = misc/config.js
|
||||
@ -37,25 +38,23 @@ nobase_dist_js_DATA = \
|
||||
ui/altTab.js \
|
||||
ui/appDisplay.js \
|
||||
ui/appFavorites.js \
|
||||
ui/automountManager.js \
|
||||
ui/autorunManager.js \
|
||||
ui/boxpointer.js \
|
||||
ui/calendar.js \
|
||||
ui/checkBox.js \
|
||||
ui/centerLayout.js \
|
||||
ui/ctrlAltTab.js \
|
||||
ui/dash.js \
|
||||
ui/dateMenu.js \
|
||||
ui/dnd.js \
|
||||
ui/endSessionDialog.js \
|
||||
ui/environment.js \
|
||||
ui/extensionSystem.js \
|
||||
ui/extensionDownloader.js \
|
||||
ui/environment.js \
|
||||
ui/flashspot.js \
|
||||
ui/ibusCandidatePopup.js\
|
||||
ui/grabHelper.js \
|
||||
ui/iconGrid.js \
|
||||
ui/keyboard.js \
|
||||
ui/keyringPrompt.js \
|
||||
ui/layout.js \
|
||||
ui/lightbox.js \
|
||||
ui/lookingGlass.js \
|
||||
@ -64,7 +63,6 @@ nobase_dist_js_DATA = \
|
||||
ui/main.js \
|
||||
ui/messageTray.js \
|
||||
ui/modalDialog.js \
|
||||
ui/networkAgent.js \
|
||||
ui/sessionMode.js \
|
||||
ui/shellEntry.js \
|
||||
ui/shellMountOperation.js \
|
||||
@ -72,9 +70,7 @@ nobase_dist_js_DATA = \
|
||||
ui/overview.js \
|
||||
ui/panel.js \
|
||||
ui/panelMenu.js \
|
||||
ui/placeDisplay.js \
|
||||
ui/pointerWatcher.js \
|
||||
ui/polkitAuthenticationAgent.js \
|
||||
ui/popupMenu.js \
|
||||
ui/remoteSearch.js \
|
||||
ui/runDialog.js \
|
||||
@ -90,7 +86,6 @@ nobase_dist_js_DATA = \
|
||||
ui/status/power.js \
|
||||
ui/status/volume.js \
|
||||
ui/status/bluetooth.js \
|
||||
ui/telepathyClient.js \
|
||||
ui/tweener.js \
|
||||
ui/unlockDialog.js \
|
||||
ui/userMenu.js \
|
||||
@ -102,4 +97,13 @@ nobase_dist_js_DATA = \
|
||||
ui/workspaceThumbnail.js \
|
||||
ui/workspacesView.js \
|
||||
ui/workspaceSwitcherPopup.js \
|
||||
ui/xdndHandler.js
|
||||
ui/xdndHandler.js \
|
||||
ui/components/__init__.js \
|
||||
ui/components/autorunManager.js \
|
||||
ui/components/automountManager.js \
|
||||
ui/components/networkAgent.js \
|
||||
ui/components/polkitAgent.js \
|
||||
ui/components/recorder.js \
|
||||
ui/components/telepathyClient.js \
|
||||
ui/components/keyring.js \
|
||||
$(NULL)
|
||||
|
@ -174,7 +174,7 @@ const Application = new Lang.Class({
|
||||
let renderer = new Gtk.CellRendererText();
|
||||
this._extensionSelector.pack_start(renderer, true);
|
||||
this._extensionSelector.add_attribute(renderer, 'text', 1);
|
||||
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null);
|
||||
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive));
|
||||
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
|
||||
|
||||
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
|
||||
|
@ -19,8 +19,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_AUTO_START |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||
|
||||
self.init(null);
|
||||
return self;
|
||||
|
@ -25,6 +25,7 @@ const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Lang = imports.lang;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Signals = imports.signals;
|
||||
@ -38,13 +39,14 @@ const GdmUtil = imports.gdm.util;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
|
||||
const _RESIZE_ANIMATION_TIME = 0.25;
|
||||
const _SCROLL_ANIMATION_TIME = 0.5;
|
||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
||||
const _LOGO_ICON_NAME_SIZE = 48;
|
||||
const _LOGO_ICON_HEIGHT = 16;
|
||||
|
||||
let _loginDialog = null;
|
||||
|
||||
@ -81,6 +83,36 @@ function _smoothlyResizeActor(actor, width, height) {
|
||||
return hold;
|
||||
}
|
||||
|
||||
const LogoMenuButton = new Lang.Class({
|
||||
Name: 'LogoMenuButton',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0, null, true);
|
||||
|
||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
|
||||
Lang.bind(this, this._updateLogo));
|
||||
|
||||
this._iconBin = new St.Bin();
|
||||
this.actor.add_actor(this._iconBin);
|
||||
|
||||
this._updateLogo();
|
||||
},
|
||||
|
||||
_updateLogo: function() {
|
||||
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
|
||||
let icon = null;
|
||||
|
||||
if (path) {
|
||||
let file = Gio.file_new_for_path(path);
|
||||
let cache = St.TextureCache.get_default();
|
||||
icon = cache.load_uri_async(file.get_uri(), -1, _LOGO_ICON_HEIGHT);
|
||||
}
|
||||
this._iconBin.set_child(icon);
|
||||
}
|
||||
});
|
||||
|
||||
const UserListItem = new Lang.Class({
|
||||
Name: 'UserListItem',
|
||||
|
||||
@ -105,6 +137,7 @@ const UserListItem = new Lang.Class({
|
||||
layout.add(textLayout, { expand: true });
|
||||
|
||||
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
|
||||
this.actor.label_actor = this._nameLabel;
|
||||
textLayout.add(this._nameLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
@ -148,14 +181,6 @@ const UserListItem = new Lang.Class({
|
||||
this.emit('activate');
|
||||
},
|
||||
|
||||
fadeOutName: function() {
|
||||
return GdmUtil.fadeOutActor(this._nameLabel);
|
||||
},
|
||||
|
||||
fadeInName: function() {
|
||||
return GdmUtil.fadeInActor(this._nameLabel);
|
||||
},
|
||||
|
||||
showTimedLoginIndicator: function(time) {
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
@ -206,16 +231,18 @@ const UserList = new Lang.Class({
|
||||
if (global.stage.get_key_focus() != this.actor)
|
||||
return;
|
||||
|
||||
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
let focusSet = this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
if (!focusSet) {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
this._moveFocusToItems();
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_showItem: function(item) {
|
||||
let tasks = [function() {
|
||||
return GdmUtil.fadeInActor(item.actor);
|
||||
},
|
||||
|
||||
function() {
|
||||
return item.fadeInName();
|
||||
}];
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
@ -279,13 +306,16 @@ const UserList = new Lang.Class({
|
||||
});
|
||||
}
|
||||
|
||||
this._box.remove_style_pseudo_class('expanded');
|
||||
let batch = new Batch.ConsecutiveBatch(this,
|
||||
[function() {
|
||||
return GdmUtil.fadeOutActor(this.actor.vscroll);
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, tasks)
|
||||
new Batch.ConcurrentBatch(this, tasks),
|
||||
|
||||
function() {
|
||||
this._box.remove_style_pseudo_class('expanded');
|
||||
}
|
||||
]);
|
||||
|
||||
return batch.run();
|
||||
@ -335,7 +365,6 @@ const UserList = new Lang.Class({
|
||||
});
|
||||
}
|
||||
|
||||
this._box.add_style_pseudo_class('expanded');
|
||||
let batch = new Batch.ConsecutiveBatch(this,
|
||||
[function() {
|
||||
this.takeOverWhitespace();
|
||||
@ -346,6 +375,10 @@ const UserList = new Lang.Class({
|
||||
return _smoothlyResizeActor(this._box, -1, fullHeight);
|
||||
},
|
||||
|
||||
function() {
|
||||
this._box.add_style_pseudo_class('expanded');
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, tasks),
|
||||
|
||||
function() {
|
||||
@ -471,6 +504,7 @@ const SessionListItem = new Lang.Class({
|
||||
|
||||
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
|
||||
text: name });
|
||||
this.actor.label_actor = label;
|
||||
|
||||
this._box.add_actor(label);
|
||||
},
|
||||
@ -625,8 +659,8 @@ const LoginDialog = new Lang.Class({
|
||||
_init: function(parentActor) {
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
parentActor: parentActor
|
||||
});
|
||||
parentActor: parentActor,
|
||||
shouldFadeIn: false });
|
||||
this.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
this.connect('opened',
|
||||
@ -635,37 +669,32 @@ const LoginDialog = new Lang.Class({
|
||||
this._userManager = AccountsService.UserManager.get_default()
|
||||
this._greeterClient = new Gdm.Client();
|
||||
|
||||
this._greeter = this._greeterClient.get_greeter_sync(null);
|
||||
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
|
||||
this._greeter = this._greeterClient.get_greeter_sync(null);
|
||||
|
||||
this._greeter.connect('default-session-name-changed',
|
||||
Lang.bind(this, this._onDefaultSessionChanged));
|
||||
this._greeter.connect('default-session-name-changed',
|
||||
Lang.bind(this, this._onDefaultSessionChanged));
|
||||
|
||||
this._greeter.connect('session-opened',
|
||||
Lang.bind(this, this._onSessionOpened));
|
||||
this._greeter.connect('timed-login-requested',
|
||||
Lang.bind(this, this._onTimedLoginRequested));
|
||||
this._greeter.connect('session-opened',
|
||||
Lang.bind(this, this._onSessionOpened));
|
||||
this._greeter.connect('timed-login-requested',
|
||||
Lang.bind(this, this._onTimedLoginRequested));
|
||||
}
|
||||
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||
|
||||
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
|
||||
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
|
||||
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
|
||||
Lang.bind(this, this._updateLogo));
|
||||
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
|
||||
Lang.bind(this, this._updateBanner));
|
||||
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY,
|
||||
Lang.bind(this, this._updateBanner));
|
||||
|
||||
this._logoBox = new St.Bin({ style_class: 'login-dialog-logo-box' });
|
||||
this.contentLayout.add(this._logoBox);
|
||||
this._updateLogo();
|
||||
|
||||
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
|
||||
text: '' });
|
||||
this.contentLayout.add(this._bannerLabel);
|
||||
@ -678,31 +707,23 @@ const LoginDialog = new Lang.Class({
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ vertical: false });
|
||||
this.contentLayout.add(mainContentBox,
|
||||
this._userList = new UserList();
|
||||
this.contentLayout.add(this._userList.actor,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: false });
|
||||
|
||||
this._userList = new UserList();
|
||||
mainContentBox.add(this._userList.actor,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
y_fill: true });
|
||||
|
||||
this.setInitialKeyFocus(this._userList.actor);
|
||||
|
||||
this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
||||
vertical: true });
|
||||
mainContentBox.add(this._promptBox,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this.contentLayout.add(this._promptBox,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
||||
|
||||
this._mainContentBox = mainContentBox;
|
||||
|
||||
this._promptBox.add(this._promptLabel,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
@ -715,6 +736,10 @@ const LoginDialog = new Lang.Class({
|
||||
x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START });
|
||||
|
||||
this._promptMessage = new St.Label({ visible: false });
|
||||
this._promptBox.add(this._promptMessage, { x_fill: true });
|
||||
|
||||
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
|
||||
this._promptLoginHint.hide();
|
||||
this._promptBox.add(this._promptLoginHint);
|
||||
@ -770,20 +795,6 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
},
|
||||
|
||||
_updateLogo: function() {
|
||||
this._logoBox.child = null;
|
||||
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
|
||||
|
||||
if (path) {
|
||||
let file = Gio.file_new_for_path(path);
|
||||
let uri = file.get_uri();
|
||||
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
this._logoBox.child = textureCache.load_uri_async(uri, -1, _LOGO_ICON_NAME_SIZE);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_updateBanner: function() {
|
||||
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
|
||||
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
|
||||
@ -797,11 +808,12 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onReset: function(client, serviceName) {
|
||||
this._promptMessage.hide();
|
||||
|
||||
let tasks = [this._hidePrompt,
|
||||
|
||||
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
|
||||
this._fadeInNotListedButton,
|
||||
this._fadeInLogo]),
|
||||
this._fadeInNotListedButton]),
|
||||
|
||||
function() {
|
||||
this._sessionList.close();
|
||||
@ -826,6 +838,16 @@ const LoginDialog = new Lang.Class({
|
||||
this._sessionList.setActiveSession(sessionId);
|
||||
},
|
||||
|
||||
_showMessage: function(userVerifier, message, styleClass) {
|
||||
if (message) {
|
||||
this._promptMessage.text = message;
|
||||
this._promptMessage.styleClass = styleClass;
|
||||
GdmUtil.fadeInActor(this._promptMessage);
|
||||
} else {
|
||||
GdmUtil.fadeOutActor(this._promptMessage);
|
||||
}
|
||||
},
|
||||
|
||||
_showLoginHint: function(verifier, message) {
|
||||
this._promptLoginHint.set_text(message)
|
||||
GdmUtil.fadeInActor(this._promptLoginHint);
|
||||
@ -888,15 +910,6 @@ const LoginDialog = new Lang.Class({
|
||||
label: C_("button", "Sign In"),
|
||||
default: true }];
|
||||
|
||||
this._promptEntryActivateCallbackId = this._promptEntry.clutter_text.connect('activate',
|
||||
Lang.bind(this, function() {
|
||||
hold.release();
|
||||
}));
|
||||
hold.connect('release', Lang.bind(this, function() {
|
||||
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
|
||||
this._promptEntryActivateCallbackId = null;
|
||||
}));
|
||||
|
||||
let tasks = [function() {
|
||||
return this._fadeInPrompt();
|
||||
},
|
||||
@ -913,11 +926,6 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_hidePrompt: function() {
|
||||
if (this._promptEntryActivateCallbackId) {
|
||||
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateCallbackId);
|
||||
this._promptEntryActivateCallbackId = null;
|
||||
}
|
||||
|
||||
this.setButtons([]);
|
||||
|
||||
let tasks = [function() {
|
||||
@ -1081,10 +1089,6 @@ const LoginDialog = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this._userVerifier.cancel();
|
||||
},
|
||||
|
||||
_onNotListedClicked: function(user) {
|
||||
let tasks = [function() {
|
||||
return this._userList.hideItems();
|
||||
@ -1099,8 +1103,7 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
|
||||
this._fadeOutNotListedButton,
|
||||
this._fadeOutLogo]),
|
||||
this._fadeOutNotListedButton]),
|
||||
|
||||
function() {
|
||||
let hold = new Batch.Hold();
|
||||
@ -1113,14 +1116,6 @@ const LoginDialog = new Lang.Class({
|
||||
batch.run();
|
||||
},
|
||||
|
||||
_fadeInLogo: function() {
|
||||
return GdmUtil.fadeInActor(this._logoBox);
|
||||
},
|
||||
|
||||
_fadeOutLogo: function() {
|
||||
return GdmUtil.fadeOutActor(this._logoBox);
|
||||
},
|
||||
|
||||
_fadeInBanner: function() {
|
||||
return GdmUtil.fadeInActor(this._bannerLabel);
|
||||
},
|
||||
@ -1168,13 +1163,8 @@ const LoginDialog = new Lang.Class({
|
||||
return this._userList.giveUpWhitespace();
|
||||
},
|
||||
|
||||
function() {
|
||||
return activatedItem.fadeOutName();
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
|
||||
this._fadeOutNotListedButton,
|
||||
this._fadeOutLogo]),
|
||||
this._fadeOutNotListedButton]),
|
||||
|
||||
function() {
|
||||
return this._userList.shrinkToNaturalHeight();
|
||||
@ -1227,7 +1217,7 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onOpened: function() {
|
||||
Main.ctrlAltTabManager.addGroup(this._mainContentBox,
|
||||
Main.ctrlAltTabManager.addGroup(this.dialogLayout,
|
||||
_("Login Window"),
|
||||
'dialog-password',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
|
||||
|
@ -18,11 +18,12 @@
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
@ -31,16 +32,16 @@ const PowerMenuButton = new Lang.Class({
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('system-shutdown-symbolic', null);
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
/* Translators: accessible name of the power menu in the login screen */
|
||||
this.parent('system-shutdown-symbolic', _("Power"));
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
|
||||
this._createSubMenu();
|
||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
this._settings.connect('changed::disable-restart-buttons',
|
||||
Lang.bind(this, this._updateVisibility));
|
||||
|
||||
this._upClient.connect('notify::can-suspend',
|
||||
Lang.bind(this, this._updateHaveSuspend));
|
||||
this._updateHaveSuspend();
|
||||
this._createSubMenu();
|
||||
|
||||
// ConsoleKit doesn't send notifications when shutdown/reboot
|
||||
// are disabled, so we update the menu item each time the menu opens
|
||||
@ -49,15 +50,17 @@ const PowerMenuButton = new Lang.Class({
|
||||
if (open) {
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveRestart();
|
||||
this._updateHaveSuspend();
|
||||
}
|
||||
}));
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveRestart();
|
||||
this._updateHaveSuspend();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
||||
this.actor.visible = shouldBeVisible;
|
||||
this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
|
||||
},
|
||||
|
||||
_updateHaveShutdown: function() {
|
||||
@ -77,9 +80,11 @@ const PowerMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateHaveSuspend: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
this._suspendItem.actor.visible = this._haveSuspend;
|
||||
this._updateVisibility();
|
||||
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._suspendItem.actor.visible = this._haveSuspend;
|
||||
this._updateVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_createSubMenu: function() {
|
||||
@ -102,8 +107,10 @@ const PowerMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_onActivateSuspend: function() {
|
||||
if (this._haveSuspend)
|
||||
this._upClient.suspend_sync(null);
|
||||
if (!this._haveSuspend)
|
||||
return;
|
||||
|
||||
this._loginManager.suspend();
|
||||
},
|
||||
|
||||
_onActivateRestart: function() {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Batch = imports.gdm.batch;
|
||||
@ -19,6 +20,7 @@ const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
||||
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||
|
||||
const LOGO_KEY = 'logo';
|
||||
|
||||
@ -81,6 +83,8 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
this._fprintManager = new Fprint.FprintManager();
|
||||
this._realmManager = new Realmd.Manager();
|
||||
|
||||
this._failCounter = 0;
|
||||
},
|
||||
|
||||
begin: function(userName, hold) {
|
||||
@ -121,6 +125,9 @@ const ShellUserVerifier = new Lang.Class({
|
||||
},
|
||||
|
||||
answerQuery: function(serviceName, answer) {
|
||||
// Clear any previous message
|
||||
this.emit('show-message', null, null);
|
||||
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
},
|
||||
|
||||
@ -137,34 +144,43 @@ const ShellUserVerifier = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_reportInitError: function(where, error) {
|
||||
logError(error, where);
|
||||
this._hold.release();
|
||||
|
||||
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
|
||||
this._verificationFailed(false);
|
||||
},
|
||||
|
||||
_reauthenticationChannelOpened: function(client, result) {
|
||||
try {
|
||||
this._userVerifier = client.open_reauthentication_channel_finish(result);
|
||||
this._connectSignals();
|
||||
this._beginVerification();
|
||||
|
||||
this._hold.release();
|
||||
} catch (e) {
|
||||
if (this._reauthOnly) {
|
||||
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
||||
return;
|
||||
|
||||
logError(e, 'Failed to open reauthentication channel');
|
||||
this.emit('verification-failed');
|
||||
this._hold.release();
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's no session running, or it otherwise fails, then fall back
|
||||
// to performing verification from this login session
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e if e.matches(Gio.DBusError, Gio.DBusError.ACCESS_DENIED) &&
|
||||
!this._reauthOnly) {
|
||||
// Gdm emits org.freedesktop.DBus.Error.AccessDenied when there is
|
||||
// no session to reauthenticate. Fall back to performing verification
|
||||
// from this login session
|
||||
client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to open reauthentication channel', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._connectSignals();
|
||||
this._beginVerification();
|
||||
this._hold.release();
|
||||
},
|
||||
|
||||
_userVerifierGot: function(client, result) {
|
||||
try {
|
||||
this._userVerifier = client.get_user_verifier_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.ErrorEnum.CANCELLED)) {
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to obtain user verifier', e);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -195,6 +211,9 @@ const ShellUserVerifier = new Lang.Class({
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start verification for user', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
@ -211,6 +230,9 @@ const ShellUserVerifier = new Lang.Class({
|
||||
obj.call_begin_verification_for_user_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start fingerprint verification for user', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
@ -224,6 +246,9 @@ const ShellUserVerifier = new Lang.Class({
|
||||
obj.call_begin_verification_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
} catch(e) {
|
||||
this._reportInitError('Failed to start verification', e);
|
||||
return;
|
||||
}
|
||||
|
||||
this._hold.release();
|
||||
@ -242,7 +267,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
// to indicate the user can swipe their finger instead
|
||||
this.emit('show-login-hint', _("(or swipe finger)"));
|
||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
Main.notifyError(info);
|
||||
this.emit('show-message', info, 'login-dialog-message-info');
|
||||
}
|
||||
},
|
||||
|
||||
@ -251,7 +276,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
// users who haven't enrolled their fingerprint.
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
Main.notifyError(problem);
|
||||
this.emit('show-message', problem, 'login-dialog-message-warning');
|
||||
},
|
||||
|
||||
_showRealmLoginHint: function() {
|
||||
@ -290,8 +315,10 @@ const ShellUserVerifier = new Lang.Class({
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
this._userVerifier.run_dispose();
|
||||
this._userVerifier = null;
|
||||
this.clear();
|
||||
|
||||
// Clear previous attempts to authenticate
|
||||
this._failCounter = 0;
|
||||
|
||||
this.emit('reset');
|
||||
},
|
||||
@ -300,12 +327,37 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this.emit('verification-complete');
|
||||
},
|
||||
|
||||
_verificationFailed: function(retry) {
|
||||
// For Not Listed / enterprise logins, immediately reset
|
||||
// the dialog
|
||||
// Otherwise, we allow ALLOWED_FAILURES attempts. After that, we
|
||||
// go back to the welcome screen.
|
||||
|
||||
this._failCounter++;
|
||||
let canRetry = retry && this._userName &&
|
||||
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||
|
||||
if (canRetry) {
|
||||
this.clear();
|
||||
this.begin(this._userName, new Batch.Hold());
|
||||
} else {
|
||||
// Allow some time to see the message, then reset everything
|
||||
Mainloop.timeout_add(3000, Lang.bind(this, function() {
|
||||
this.cancel();
|
||||
|
||||
this._onReset();
|
||||
}));
|
||||
}
|
||||
|
||||
this.emit('verification-failed');
|
||||
},
|
||||
|
||||
_onConversationStopped: function(client, serviceName) {
|
||||
// if the password service fails, then cancel everything.
|
||||
// But if, e.g., fingerprint fails, still give
|
||||
// password authentication a chance to succeed
|
||||
if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
this.emit('verification-failed');
|
||||
this._verificationFailed(true);
|
||||
}
|
||||
|
||||
this.emit('hide-login-hint');
|
||||
|
@ -120,11 +120,6 @@ function createExtensionObject(uuid, dir, type) {
|
||||
}
|
||||
}
|
||||
|
||||
// Encourage people to add this
|
||||
if (!meta.url) {
|
||||
log('Warning: Missing "url" property in %s/metadata.json'.format(uuid));
|
||||
}
|
||||
|
||||
if (uuid != meta.uuid) {
|
||||
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
|
||||
}
|
||||
@ -161,7 +156,8 @@ const ExtensionFinder = new Lang.Class({
|
||||
try {
|
||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
logError(e, 'Could not enumerate extensions directory');
|
||||
if (e.domain != Gio.io_error_quark() || e.code != Gio.IOErrorEnum.NOT_FOUND)
|
||||
logError(e, 'Could not enumerate extensions directory');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -41,24 +41,26 @@ const HistoryManager = new Lang.Class({
|
||||
this._historyIndex = this._history.length;
|
||||
},
|
||||
|
||||
prevItem: function(text) {
|
||||
_setPrevItem: function(text) {
|
||||
if (this._historyIndex <= 0)
|
||||
return text;
|
||||
return false;
|
||||
|
||||
if (text)
|
||||
this._history[this._historyIndex] = text;
|
||||
this._historyIndex--;
|
||||
return this._indexChanged();
|
||||
this._indexChanged();
|
||||
return true;
|
||||
},
|
||||
|
||||
nextItem: function(text) {
|
||||
_setNextItem: function(text) {
|
||||
if (this._historyIndex >= this._history.length)
|
||||
return text;
|
||||
return false;
|
||||
|
||||
if (text)
|
||||
this._history[this._historyIndex] = text;
|
||||
this._historyIndex++;
|
||||
return this._indexChanged();
|
||||
this._indexChanged();
|
||||
return true;
|
||||
},
|
||||
|
||||
lastItem: function() {
|
||||
@ -83,11 +85,9 @@ const HistoryManager = new Lang.Class({
|
||||
_onEntryKeyPress: function(entry, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Up) {
|
||||
this.prevItem(entry.get_text());
|
||||
return true;
|
||||
return this._setPrevItem(entry.get_text());
|
||||
} else if (symbol == Clutter.KEY_Down) {
|
||||
this.nextItem(entry.get_text());
|
||||
return true;
|
||||
return this._setNextItem(entry.get_text());
|
||||
}
|
||||
return false;
|
||||
},
|
||||
@ -98,8 +98,6 @@ const HistoryManager = new Lang.Class({
|
||||
|
||||
if (this._entry)
|
||||
this._entry.set_text(current);
|
||||
|
||||
return current;
|
||||
},
|
||||
|
||||
_save: function() {
|
||||
|
@ -3,7 +3,9 @@
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
||||
<method name='PowerOff'>
|
||||
@ -12,12 +14,18 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
|
||||
<method name='Reboot'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='Suspend'>
|
||||
<arg type='b' direction='in'/>
|
||||
</method>
|
||||
<method name='CanPowerOff'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='CanReboot'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='CanSuspend'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
@ -123,12 +131,25 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
});
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
this._proxy.CanSuspendRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback(false);
|
||||
else
|
||||
asyncCallback(result[0] != 'no');
|
||||
});
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
this._proxy.PowerOffRemote(true);
|
||||
},
|
||||
|
||||
reboot: function() {
|
||||
this._proxy.RebootRemote(true);
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this._proxy.SuspendRemote(true);
|
||||
}
|
||||
});
|
||||
|
||||
@ -139,6 +160,7 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
this._proxy = new ConsoleKitManager(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
'/org/freedesktop/ConsoleKit/Manager');
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
},
|
||||
|
||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||
@ -186,12 +208,22 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
});
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
asyncCallback(this._upClient.get_can_suspend());
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
this._proxy.StopRemote();
|
||||
},
|
||||
|
||||
reboot: function() {
|
||||
this._proxy.RestartRemote();
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this._upClient.suspend_sync(null);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const GMenu = imports.gi.GMenu;
|
||||
@ -371,6 +372,8 @@ const SettingsSearchProvider = new Lang.Class({
|
||||
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
|
||||
let appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
|
||||
this.icon = appInfo.get_icon();
|
||||
},
|
||||
|
||||
getResultMetas: function(prefs, callback) {
|
||||
@ -379,9 +382,7 @@ const SettingsSearchProvider = new Lang.Class({
|
||||
let pref = prefs[i];
|
||||
metas.push({ 'id': pref,
|
||||
'name': pref.get_name(),
|
||||
'createIcon': function(size) {
|
||||
return pref.create_icon_texture(size);
|
||||
}
|
||||
'createIcon': function(size) { return; }
|
||||
});
|
||||
}
|
||||
callback(metas);
|
||||
@ -461,15 +462,15 @@ const AppWellIcon = new Lang.Class({
|
||||
this._draggable.connect('drag-begin', Lang.bind(this,
|
||||
function () {
|
||||
this._removeMenuTimeout();
|
||||
Main.overview.beginItemDrag(this);
|
||||
Main.overview.beginAppDrag(this);
|
||||
}));
|
||||
this._draggable.connect('drag-cancelled', Lang.bind(this,
|
||||
function () {
|
||||
Main.overview.cancelledItemDrag(this);
|
||||
Main.overview.cancelledAppDrag(this);
|
||||
}));
|
||||
this._draggable.connect('drag-end', Lang.bind(this,
|
||||
function () {
|
||||
Main.overview.endItemDrag(this);
|
||||
Main.overview.endAppDrag(this);
|
||||
}));
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
@ -141,6 +141,7 @@ const BoxPointer = new Lang.Class({
|
||||
|
||||
this._muteInput();
|
||||
|
||||
Tweener.removeTweens(this);
|
||||
Tweener.addTween(this, { opacity: fade ? 0 : 255,
|
||||
xOffset: xOffset,
|
||||
yOffset: yOffset,
|
||||
@ -274,14 +275,51 @@ const BoxPointer = new Lang.Class({
|
||||
let [x1, y1] = [halfBorder, halfBorder];
|
||||
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
||||
|
||||
let skipTopLeft = false;
|
||||
let skipTopRight = false;
|
||||
let skipBottomLeft = false;
|
||||
let skipBottomRight = false;
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (this._arrowOrigin == x1)
|
||||
skipTopLeft = true;
|
||||
else if (this._arrowOrigin == x2)
|
||||
skipTopRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.RIGHT:
|
||||
if (this._arrowOrigin == y1)
|
||||
skipTopRight = true;
|
||||
else if (this._arrowOrigin == y2)
|
||||
skipBottomRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.BOTTOM:
|
||||
if (this._arrowOrigin == x1)
|
||||
skipBottomLeft = true;
|
||||
else if (this._arrowOrigin == x2)
|
||||
skipBottomRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
if (this._arrowOrigin == y1)
|
||||
skipTopLeft = true;
|
||||
else if (this._arrowOrigin == y2)
|
||||
skipBottomLeft = true;
|
||||
break;
|
||||
}
|
||||
|
||||
cr.moveTo(x1 + borderRadius, y1);
|
||||
if (this._arrowSide == St.Side.TOP) {
|
||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
|
||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
if (skipTopLeft) {
|
||||
cr.moveTo(x1, y2 - borderRadius);
|
||||
cr.lineTo(x1, y1 - rise);
|
||||
cr.lineTo(x1 + halfBase, y1);
|
||||
} else if (skipTopRight) {
|
||||
cr.lineTo(x2 - halfBase, y1);
|
||||
cr.lineTo(x2, y1 - rise);
|
||||
cr.lineTo(x2, y1 + borderRadius);
|
||||
} else {
|
||||
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
@ -289,19 +327,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
cr.lineTo(x2 - borderRadius, y1);
|
||||
|
||||
// top-right corner
|
||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||
3*Math.PI/2, Math.PI*2);
|
||||
if (!skipTopRight) {
|
||||
cr.lineTo(x2 - borderRadius, y1);
|
||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||
3*Math.PI/2, Math.PI*2);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.RIGHT) {
|
||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
if (skipTopRight) {
|
||||
cr.lineTo(x2 + rise, y1);
|
||||
cr.lineTo(x2 + rise, y1 + halfBase);
|
||||
} else if (skipBottomRight) {
|
||||
cr.lineTo(x2, y2 - halfBase);
|
||||
cr.lineTo(x2 + rise, y2);
|
||||
cr.lineTo(x2 - borderRadius, y2);
|
||||
} else {
|
||||
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
@ -309,19 +348,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
cr.lineTo(x2, y2 - borderRadius);
|
||||
|
||||
// bottom-right corner
|
||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||
0, Math.PI/2);
|
||||
if (!skipBottomRight) {
|
||||
cr.lineTo(x2, y2 - borderRadius);
|
||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||
0, Math.PI/2);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.BOTTOM) {
|
||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
|
||||
if (skipBottomLeft) {
|
||||
cr.lineTo(x1 + halfBase, y2);
|
||||
cr.lineTo(x1, y2 + rise);
|
||||
cr.lineTo(x1, y2 - borderRadius);
|
||||
} else if (skipBottomRight) {
|
||||
cr.lineTo(x2, y2 + rise);
|
||||
cr.lineTo(x2 - halfBase, y2);
|
||||
} else {
|
||||
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
@ -329,19 +369,20 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
cr.lineTo(x1 + borderRadius, y2);
|
||||
|
||||
// bottom-left corner
|
||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||
Math.PI/2, Math.PI);
|
||||
if (!skipBottomLeft) {
|
||||
cr.lineTo(x1 + borderRadius, y2);
|
||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||
Math.PI/2, Math.PI);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.LEFT) {
|
||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
||||
if (skipTopLeft) {
|
||||
cr.lineTo(x1, y1 + halfBase);
|
||||
cr.lineTo(x1 - rise, y1);
|
||||
cr.lineTo(x1 + borderRadius, y1);
|
||||
} else if (skipBottomLeft) {
|
||||
cr.lineTo(x1 - rise, y2)
|
||||
cr.lineTo(x1 - rise, y2 - halfBase);
|
||||
} else {
|
||||
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
@ -349,11 +390,11 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
cr.lineTo(x1, y1 + borderRadius);
|
||||
|
||||
// top-left corner
|
||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||
Math.PI, 3*Math.PI/2);
|
||||
if (!skipTopLeft) {
|
||||
cr.lineTo(x1, y1 + borderRadius);
|
||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||
Math.PI, 3*Math.PI/2);
|
||||
}
|
||||
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
cr.fillPreserve();
|
||||
@ -406,9 +447,7 @@ const BoxPointer = new Lang.Class({
|
||||
let arrowBase = themeNode.get_length('-arrow-base');
|
||||
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
||||
let halfMargin = margin / 2;
|
||||
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let gap = themeNode.get_length('-boxpointer-gap');
|
||||
let padding = themeNode.get_length('-arrow-rise');
|
||||
|
||||
@ -429,8 +468,24 @@ const BoxPointer = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
|
||||
// Now align and position the pointing axis, making sure
|
||||
// it fits on screen
|
||||
// Now align and position the pointing axis, making sure it fits on
|
||||
// screen. If the arrowOrigin is so close to the edge that the arrow
|
||||
// will not be isosceles, we try to compensate as follows:
|
||||
// - We skip the rounded corner and settle for a right angled arrow
|
||||
// as shown below. See _drawBorder for further details.
|
||||
// |\_____
|
||||
// |
|
||||
// |
|
||||
// - If the arrow was going to be acute angled, we move the position
|
||||
// of the box to maintain the arrow's accuracy.
|
||||
|
||||
let arrowOrigin;
|
||||
let halfBase = Math.floor(arrowBase/2);
|
||||
let halfBorder = borderWidth / 2;
|
||||
let halfMargin = margin / 2;
|
||||
let [x1, y1] = [halfBorder, halfBorder];
|
||||
let [x2, y2] = [natWidth - halfBorder, natHeight - halfBorder];
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
case St.Side.BOTTOM:
|
||||
@ -438,7 +493,17 @@ const BoxPointer = new Lang.Class({
|
||||
|
||||
resX = Math.max(resX, monitor.x + padding);
|
||||
resX = Math.min(resX, monitor.x + monitor.width - (padding + natWidth));
|
||||
this.setArrowOrigin(sourceCenterX - resX);
|
||||
|
||||
arrowOrigin = sourceCenterX - resX;
|
||||
if (arrowOrigin <= (x1 + (borderRadius + halfBase))) {
|
||||
if (arrowOrigin > x1)
|
||||
resX += (arrowOrigin - x1);
|
||||
arrowOrigin = x1;
|
||||
} else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) {
|
||||
if (arrowOrigin < x2)
|
||||
resX -= (x2 - arrowOrigin);
|
||||
arrowOrigin = x2;
|
||||
}
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
@ -448,10 +513,21 @@ const BoxPointer = new Lang.Class({
|
||||
resY = Math.max(resY, monitor.y + padding);
|
||||
resY = Math.min(resY, monitor.y + monitor.height - (padding + natHeight));
|
||||
|
||||
this.setArrowOrigin(sourceCenterY - resY);
|
||||
arrowOrigin = sourceCenterY - resY;
|
||||
if (arrowOrigin <= (y1 + (borderRadius + halfBase))) {
|
||||
if (arrowOrigin > y1)
|
||||
resY += (arrowOrigin - y1);
|
||||
arrowOrigin = y1;
|
||||
} else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) {
|
||||
if (arrowOrigin < y2)
|
||||
resX -= (y2 - arrowOrigin);
|
||||
arrowOrigin = y2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.setArrowOrigin(arrowOrigin);
|
||||
|
||||
let parent = this.actor.get_parent();
|
||||
let success, x, y;
|
||||
while (!success) {
|
||||
|
@ -270,8 +270,9 @@ const DBusEventSource = new Lang.Class({
|
||||
this._loadEvents(false);
|
||||
},
|
||||
|
||||
_onEventsReceived: function([appointments]) {
|
||||
_onEventsReceived: function(results, error) {
|
||||
let newEvents = [];
|
||||
let appointments = results ? results[0] : null;
|
||||
if (appointments != null) {
|
||||
for (let n = 0; n < appointments.length; n++) {
|
||||
let a = appointments[n];
|
||||
@ -339,22 +340,10 @@ const DBusEventSource = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(DBusEventSource.prototype);
|
||||
|
||||
// Calendar:
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||
const Calendar = new Lang.Class({
|
||||
Name: 'Calendar',
|
||||
|
||||
_init: function(eventSource) {
|
||||
if (eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
|
||||
this._eventSource.connect('changed', Lang.bind(this,
|
||||
function() {
|
||||
this._update(false);
|
||||
}));
|
||||
}
|
||||
|
||||
_init: function() {
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
this._weekdate = NaN;
|
||||
this._digitWidth = NaN;
|
||||
@ -391,6 +380,24 @@ const Calendar = new Lang.Class({
|
||||
this._buildHeader ();
|
||||
},
|
||||
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||
setEventSource: function(eventSource) {
|
||||
if (this._eventSource) {
|
||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
||||
this._eventSource = null;
|
||||
}
|
||||
|
||||
this._eventSource = eventSource;
|
||||
|
||||
if (this._eventSource) {
|
||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||
this._update(false);
|
||||
}));
|
||||
this._update(true);
|
||||
}
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date, forceReload) {
|
||||
if (!_sameDay(date, this._selectedDate)) {
|
||||
@ -621,16 +628,25 @@ Signals.addSignalMethods(Calendar.prototype);
|
||||
const EventsList = new Lang.Class({
|
||||
Name: 'EventsList',
|
||||
|
||||
_init: function(eventSource) {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
||||
this._date = new Date();
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
},
|
||||
|
||||
this._update();
|
||||
setEventSource: function(eventSource) {
|
||||
if (this._eventSource) {
|
||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
||||
this._eventSource = null;
|
||||
}
|
||||
|
||||
this._eventSource = eventSource;
|
||||
|
||||
if (this._eventSource) {
|
||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
}
|
||||
},
|
||||
|
||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
||||
|
93
js/ui/centerLayout.js
Normal file
93
js/ui/centerLayout.js
Normal file
@ -0,0 +1,93 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
|
||||
function connectLayoutManager(layoutManager, styleChanged) {
|
||||
let widget, styleChangedId;
|
||||
|
||||
function _styleChanged() {
|
||||
let themeNode = widget.get_theme_node();
|
||||
styleChanged(themeNode, widget);
|
||||
}
|
||||
|
||||
function actorChanged() {
|
||||
if (widget) {
|
||||
widget.disconnect(styleChangedId);
|
||||
styleChangedId = 0;
|
||||
}
|
||||
|
||||
let actor = layoutManager.get_actor();
|
||||
if (actor && actor instanceof St.Widget) {
|
||||
widget = actor;
|
||||
styleChangedId = widget.connect('style-changed', _styleChanged);
|
||||
_styleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
layoutManager.connect('notify::actor', actorChanged);
|
||||
return layoutManager;
|
||||
}
|
||||
|
||||
function connectSpacing(layoutManager) {
|
||||
return connectLayoutManager(layoutManager, function(themeNode, widget) {
|
||||
layoutManager.spacing = themeNode.get_length('spacing');
|
||||
});
|
||||
}
|
||||
|
||||
const CenterLayout = new Lang.Class({
|
||||
Name: 'CenterLayout',
|
||||
Extends: Clutter.BoxLayout,
|
||||
|
||||
vfunc_allocate: function(container, box, flags) {
|
||||
let rtl = container.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
// Assume that these are the first three widgets and they are all visible.
|
||||
let [left, center, right] = container.get_children();
|
||||
|
||||
// Only support horizontal layouts for now.
|
||||
let [centerMinWidth, centerNaturalWidth] = center.get_preferred_width(availWidth);
|
||||
|
||||
let sideWidth = (availWidth - centerNaturalWidth) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y1 + availHeight;
|
||||
|
||||
if (left) {
|
||||
let [leftMinWidth, leftNaturalWidth] = left.get_preferred_width(availWidth);
|
||||
let leftSide = Math.min(Math.floor(sideWidth), leftNaturalWidth);
|
||||
if (rtl) {
|
||||
childBox.x1 = availWidth - leftSide;
|
||||
childBox.x2 = availWidth;
|
||||
} else {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = leftSide;
|
||||
}
|
||||
childBox.x1 += box.x1;
|
||||
left.allocate(childBox, flags);
|
||||
}
|
||||
|
||||
childBox.x1 = box.x1 + Math.ceil(sideWidth);
|
||||
childBox.x2 = childBox.x1 + centerNaturalWidth;
|
||||
center.allocate(childBox, flags);
|
||||
|
||||
if (right) {
|
||||
let [rightMinWidth, rightNaturalWidth] = right.get_preferred_width(availWidth);
|
||||
let rightSide = Math.min(Math.floor(sideWidth), rightNaturalWidth);
|
||||
if (rtl) {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = rightSide;
|
||||
} else {
|
||||
childBox.x1 = availWidth - rightSide;
|
||||
childBox.x2 = availWidth;
|
||||
}
|
||||
childBox.x1 += box.x1;
|
||||
right.allocate(childBox, flags);
|
||||
}
|
||||
}
|
||||
});
|
65
js/ui/components/__init__.js
Normal file
65
js/ui/components/__init__.js
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ComponentManager = new Lang.Class({
|
||||
Name: 'ComponentManager',
|
||||
|
||||
_init: function() {
|
||||
this._allComponents = {};
|
||||
this._enabledComponents = [];
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
let newEnabledComponents = Main.sessionMode.components;
|
||||
|
||||
newEnabledComponents.filter(Lang.bind(this, function(name) {
|
||||
return this._enabledComponents.indexOf(name) == -1;
|
||||
})).forEach(Lang.bind(this, function(name) {
|
||||
this._enableComponent(name);
|
||||
}));
|
||||
|
||||
this._enabledComponents.filter(Lang.bind(this, function(name) {
|
||||
return newEnabledComponents.indexOf(name) == -1;
|
||||
})).forEach(Lang.bind(this, function(name) {
|
||||
this._disableComponent(name);
|
||||
}));
|
||||
|
||||
this._enabledComponents = newEnabledComponents;
|
||||
},
|
||||
|
||||
_importComponent: function(name) {
|
||||
let module = imports.ui.components[name];
|
||||
return module.Component;
|
||||
},
|
||||
|
||||
_ensureComponent: function(name) {
|
||||
let component = this._allComponents[name];
|
||||
if (component)
|
||||
return component;
|
||||
|
||||
if (Main.sessionMode.isLocked)
|
||||
return null;
|
||||
|
||||
let constructor = this._importComponent(name);
|
||||
component = new constructor();
|
||||
this._allComponents[name] = component;
|
||||
return component;
|
||||
},
|
||||
|
||||
_enableComponent: function(name) {
|
||||
let component = this._ensureComponent(name);
|
||||
if (component)
|
||||
component.enable();
|
||||
},
|
||||
|
||||
_disableComponent: function(name) {
|
||||
let component = this._allComponents[name];
|
||||
if (component == null)
|
||||
return;
|
||||
component.disable();
|
||||
}
|
||||
});
|
@ -34,28 +34,30 @@ const AutomountManager = new Lang.Class({
|
||||
this._inhibited = false;
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
|
||||
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._lockStatusChanged));
|
||||
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
},
|
||||
|
||||
this._volumeMonitor.connect('volume-added',
|
||||
Lang.bind(this,
|
||||
this._onVolumeAdded));
|
||||
this._volumeMonitor.connect('volume-removed',
|
||||
Lang.bind(this,
|
||||
this._onVolumeRemoved));
|
||||
this._volumeMonitor.connect('drive-connected',
|
||||
Lang.bind(this,
|
||||
this._onDriveConnected));
|
||||
this._volumeMonitor.connect('drive-disconnected',
|
||||
Lang.bind(this,
|
||||
this._onDriveDisconnected));
|
||||
this._volumeMonitor.connect('drive-eject-button',
|
||||
Lang.bind(this,
|
||||
this._onDriveEjectButton));
|
||||
enable: function() {
|
||||
this._volumeAddedId = this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded));
|
||||
this._volumeRemovedId = this._volumeMonitor.connect('volume-removed', Lang.bind(this, this._onVolumeRemoved));
|
||||
this._driveConnectedId = this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected));
|
||||
this._driveDisconnectedId = this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected));
|
||||
this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
|
||||
|
||||
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._volumeMonitor.disconnect(this._volumeAddedId);
|
||||
this._volumeMonitor.disconnect(this._volumeRemovedId);
|
||||
this._volumeMonitor.disconnect(this._driveConnectedId);
|
||||
this._volumeMonitor.disconnect(this._driveDisconnectedId);
|
||||
this._volumeMonitor.disconnect(this._driveEjectButtonId);
|
||||
|
||||
if (this._mountAllId > 0) {
|
||||
Mainloop.source_remove(this._mountAllId);
|
||||
this._mountAllId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
|
||||
@ -68,17 +70,6 @@ const AutomountManager = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_lockStatusChanged: function(shield, locked) {
|
||||
if (!locked) {
|
||||
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
|
||||
this._checkAndMountVolume(volume);
|
||||
}));
|
||||
}
|
||||
|
||||
// clear the queue anyway
|
||||
this._volumeQueue = [];
|
||||
},
|
||||
|
||||
_startupMountAll: function() {
|
||||
let volumes = this._volumeMonitor.get_volumes();
|
||||
volumes.forEach(Lang.bind(this, function(volume) {
|
||||
@ -87,6 +78,7 @@ const AutomountManager = new Lang.Class({
|
||||
allowAutorun: false });
|
||||
}));
|
||||
|
||||
this._mountAllId = 0;
|
||||
return false;
|
||||
},
|
||||
|
||||
@ -96,9 +88,6 @@ const AutomountManager = new Lang.Class({
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
if (Main.screenShield.locked)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-added-media');
|
||||
},
|
||||
|
||||
@ -108,9 +97,6 @@ const AutomountManager = new Lang.Class({
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
if (Main.screenShield.locked)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-removed-media');
|
||||
},
|
||||
|
||||
@ -159,13 +145,6 @@ const AutomountManager = new Lang.Class({
|
||||
// don't attempt automount
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
if (Main.screenShield.locked) {
|
||||
if (this._volumeQueue.indexOf(volume) == -1)
|
||||
this._volumeQueue.push(volume);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inhibited)
|
||||
@ -259,3 +238,4 @@ const AutomountManager = new Lang.Class({
|
||||
});
|
||||
}
|
||||
});
|
||||
const Component = AutomountManager;
|
@ -44,6 +44,17 @@ function isMountRootHidden(root) {
|
||||
return (path.indexOf('/.') != -1);
|
||||
}
|
||||
|
||||
function isMountNonLocal(mount) {
|
||||
// If the mount doesn't have an associated volume, that means it's
|
||||
// an uninteresting filesystem. Most devices that we care about will
|
||||
// have a mount, like media players and USB sticks.
|
||||
let volume = mount.get_volume();
|
||||
if (volume == null)
|
||||
return true;
|
||||
|
||||
return (volume.get_identifier("class") == "network");
|
||||
}
|
||||
|
||||
function startAppForMount(app, mount) {
|
||||
let files = [];
|
||||
let root = mount.get_root();
|
||||
@ -83,13 +94,21 @@ const ContentTypeDiscoverer = new Lang.Class({
|
||||
|
||||
_init: function(callback) {
|
||||
this._callback = callback;
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
},
|
||||
|
||||
guessContentTypes: function(mount) {
|
||||
// guess mount's content types using GIO
|
||||
mount.guess_content_type(false, null,
|
||||
Lang.bind(this,
|
||||
this._onContentTypeGuessed));
|
||||
let autorunEnabled = !this._settings.get_boolean(SETTING_DISABLE_AUTORUN);
|
||||
let shouldScan = autorunEnabled && !isMountNonLocal(mount);
|
||||
|
||||
if (shouldScan) {
|
||||
// guess mount's content types using GIO
|
||||
mount.guess_content_type(false, null,
|
||||
Lang.bind(this,
|
||||
this._onContentTypeGuessed));
|
||||
} else {
|
||||
this._emitCallback(mount, []);
|
||||
}
|
||||
},
|
||||
|
||||
_onContentTypeGuessed: function(mount, res) {
|
||||
@ -147,33 +166,50 @@ const AutorunManager = new Lang.Class({
|
||||
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
|
||||
this._volumeMonitor.connect('mount-added',
|
||||
Lang.bind(this,
|
||||
this._onMountAdded));
|
||||
this._volumeMonitor.connect('mount-removed',
|
||||
Lang.bind(this,
|
||||
this._onMountRemoved));
|
||||
this._transDispatcher = new AutorunTransientDispatcher(this);
|
||||
},
|
||||
|
||||
this._transDispatcher = new AutorunTransientDispatcher();
|
||||
this._createResidentSource();
|
||||
_ensureResidentSource: function() {
|
||||
if (this._residentSource)
|
||||
return;
|
||||
|
||||
let mounts = this._volumeMonitor.get_mounts();
|
||||
|
||||
mounts.forEach(Lang.bind(this, function (mount) {
|
||||
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
||||
function (mount, apps) {
|
||||
this._residentSource.addMount(mount, apps);
|
||||
}));
|
||||
|
||||
discoverer.guessContentTypes(mount);
|
||||
this._residentSource = new AutorunResidentSource(this);
|
||||
let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
|
||||
this._residentSource.disconnect(destroyId);
|
||||
this._residentSource = null;
|
||||
}));
|
||||
},
|
||||
|
||||
_createResidentSource: function() {
|
||||
this._residentSource = new AutorunResidentSource();
|
||||
this._residentSource.connect('destroy',
|
||||
Lang.bind(this,
|
||||
this._createResidentSource));
|
||||
enable: function() {
|
||||
this._scanMounts();
|
||||
|
||||
this._mountAddedId = this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded));
|
||||
this._mountRemovedId = this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved));
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
if (this._residentSource)
|
||||
this._residentSource.destroy();
|
||||
this._volumeMonitor.disconnect(this._mountAddedId);
|
||||
this._volumeMonitor.disconnect(this._mountRemovedId);
|
||||
},
|
||||
|
||||
_processMount: function(mount, hotplug) {
|
||||
let discoverer = new ContentTypeDiscoverer(Lang.bind(this, function(mount, apps, contentTypes) {
|
||||
this._ensureResidentSource();
|
||||
this._residentSource.addMount(mount, apps);
|
||||
|
||||
if (hotplug)
|
||||
this._transDispatcher.addMount(mount, apps, contentTypes);
|
||||
}));
|
||||
discoverer.guessContentTypes(mount);
|
||||
},
|
||||
|
||||
_scanMounts: function() {
|
||||
let mounts = this._volumeMonitor.get_mounts();
|
||||
mounts.forEach(Lang.bind(this, function(mount) {
|
||||
this._processMount(mount, false);
|
||||
}));
|
||||
},
|
||||
|
||||
_onMountAdded: function(monitor, mount) {
|
||||
@ -182,18 +218,13 @@ const AutorunManager = new Lang.Class({
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
|
||||
function (mount, apps, contentTypes) {
|
||||
this._transDispatcher.addMount(mount, apps, contentTypes);
|
||||
this._residentSource.addMount(mount, apps);
|
||||
}));
|
||||
|
||||
discoverer.guessContentTypes(mount);
|
||||
this._processMount(mount, true);
|
||||
},
|
||||
|
||||
_onMountRemoved: function(monitor, mount) {
|
||||
this._transDispatcher.removeMount(mount);
|
||||
this._residentSource.removeMount(mount);
|
||||
if (this._residentSource)
|
||||
this._residentSource.removeMount(mount);
|
||||
},
|
||||
|
||||
ejectMount: function(mount) {
|
||||
@ -260,13 +291,18 @@ const AutorunResidentSource = new Lang.Class({
|
||||
Name: 'AutorunResidentSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function() {
|
||||
_init: function(manager) {
|
||||
this.parent(_("Removable Devices"), 'media-removable');
|
||||
this.showInLockScreen = false;
|
||||
|
||||
this._mounts = [];
|
||||
|
||||
this._notification = new AutorunResidentNotification(this);
|
||||
this._manager = manager;
|
||||
this._notification = new AutorunResidentNotification(this._manager, this);
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
addMount: function(mount, apps) {
|
||||
@ -316,7 +352,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
Name: 'AutorunResidentNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source) {
|
||||
_init: function(manager, source) {
|
||||
this.parent(source, source.title, null, { customContent: true });
|
||||
|
||||
// set the notification as resident
|
||||
@ -324,6 +360,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
|
||||
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
|
||||
vertical: true });
|
||||
this._manager = manager;
|
||||
|
||||
this.addActor(this._layout,
|
||||
{ x_expand: true,
|
||||
@ -371,7 +408,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
expand: true });
|
||||
|
||||
let ejectIcon =
|
||||
new St.Icon({ icon_name: 'media-eject',
|
||||
new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||
style_class: 'hotplug-resident-eject-icon' });
|
||||
|
||||
let ejectButton =
|
||||
@ -386,7 +423,7 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
}));
|
||||
|
||||
ejectButton.connect('clicked', Lang.bind(this, function() {
|
||||
Main.autorunManager.ejectMount(mount);
|
||||
this._manager.ejectMount(mount);
|
||||
}));
|
||||
|
||||
return item;
|
||||
@ -396,7 +433,8 @@ const AutorunResidentNotification = new Lang.Class({
|
||||
const AutorunTransientDispatcher = new Lang.Class({
|
||||
Name: 'AutorunTransientDispatcher',
|
||||
|
||||
_init: function() {
|
||||
_init: function(manager) {
|
||||
this._manager = manager;
|
||||
this._sources = [];
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
},
|
||||
@ -439,7 +477,7 @@ const AutorunTransientDispatcher = new Lang.Class({
|
||||
return;
|
||||
|
||||
// add a new source
|
||||
this._sources.push(new AutorunTransientSource(mount, apps));
|
||||
this._sources.push(new AutorunTransientSource(this._manager, mount, apps));
|
||||
},
|
||||
|
||||
addMount: function(mount, apps, contentTypes) {
|
||||
@ -492,22 +530,22 @@ const AutorunTransientSource = new Lang.Class({
|
||||
Name: 'AutorunTransientSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(mount, apps) {
|
||||
_init: function(manager, mount, apps) {
|
||||
this._manager = manager;
|
||||
this.mount = mount;
|
||||
this.apps = apps;
|
||||
|
||||
this.parent(mount.get_name());
|
||||
|
||||
this._notification = new AutorunTransientNotification(this);
|
||||
this._notification = new AutorunTransientNotification(this._manager, this);
|
||||
|
||||
// add ourselves as a source, and popup the notification
|
||||
Main.messageTray.add(this);
|
||||
this.notify(this._notification);
|
||||
},
|
||||
|
||||
createIcon: function(size) {
|
||||
return new St.Icon({ gicon: this.mount.get_icon(),
|
||||
icon_size: size });
|
||||
getIcon: function() {
|
||||
return this.mount.get_icon();
|
||||
}
|
||||
});
|
||||
|
||||
@ -515,9 +553,10 @@ const AutorunTransientNotification = new Lang.Class({
|
||||
Name: 'AutorunTransientNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source) {
|
||||
_init: function(manager, source) {
|
||||
this.parent(source, source.title, null, { customContent: true });
|
||||
|
||||
this._manager = manager;
|
||||
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
|
||||
vertical: true });
|
||||
this.addActor(this._box);
|
||||
@ -569,7 +608,7 @@ const AutorunTransientNotification = new Lang.Class({
|
||||
|
||||
_buttonForEject: function() {
|
||||
let box = new St.BoxLayout();
|
||||
let icon = new St.Icon({ icon_name: 'media-eject',
|
||||
let icon = new St.Icon({ icon_name: 'media-eject-symbolic',
|
||||
style_class: 'hotplug-notification-item-icon' });
|
||||
box.add(icon);
|
||||
|
||||
@ -586,10 +625,11 @@ const AutorunTransientNotification = new Lang.Class({
|
||||
style_class: 'hotplug-notification-item' });
|
||||
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
Main.autorunManager.ejectMount(this._mount);
|
||||
this._manager.ejectMount(this._mount);
|
||||
}));
|
||||
|
||||
return button;
|
||||
}
|
||||
});
|
||||
|
||||
const Component = AutorunManager;
|
@ -192,23 +192,36 @@ const KeyringDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onContinueButton: function() {
|
||||
this.prompt.complete()
|
||||
this.prompt.complete();
|
||||
},
|
||||
|
||||
_onCancelButton: function() {
|
||||
this.prompt.cancel()
|
||||
this.prompt.cancel();
|
||||
},
|
||||
});
|
||||
|
||||
function init() {
|
||||
prompter = new Gcr.SystemPrompter();
|
||||
prompter.connect('new-prompt', function(prompter) {
|
||||
let dialog = new KeyringDialog();
|
||||
return dialog.prompt;
|
||||
});
|
||||
const KeyringPrompter = new Lang.Class({
|
||||
Name: 'KeyringPrompter',
|
||||
|
||||
let connection = Gio.DBus.session;
|
||||
prompter.register(connection);
|
||||
Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
|
||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
}
|
||||
_init: function() {
|
||||
this._prompter = new Gcr.SystemPrompter();
|
||||
this._prompter.connect('new-prompt', function(prompter) {
|
||||
let dialog = new KeyringDialog();
|
||||
return dialog.prompt;
|
||||
});
|
||||
this._dbusId = null;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._prompter.register(Gio.DBus.session);
|
||||
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._prompter.unregister(false);
|
||||
Gio.DBus.session.unown_name(this._dbusId);
|
||||
}
|
||||
});
|
||||
|
||||
const Component = KeyringPrompter;
|
@ -1,23 +1,4 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
/*
|
||||
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
@ -427,7 +408,10 @@ const VPNRequestHandler = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
cancel: function(respond) {
|
||||
if (respond)
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||
|
||||
if (this._newStylePlugin && this._shellDialog) {
|
||||
this._shellDialog.close(global.get_current_time());
|
||||
this._shellDialog.destroy();
|
||||
@ -603,7 +587,7 @@ const NetworkAgent = new Lang.Class({
|
||||
Name: 'NetworkAgent',
|
||||
|
||||
_init: function() {
|
||||
this._native = new Shell.NetworkAgent({ auto_register: true,
|
||||
this._native = new Shell.NetworkAgent({ auto_register: false,
|
||||
identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||
|
||||
this._dialogs = { };
|
||||
@ -613,6 +597,24 @@ const NetworkAgent = new Lang.Class({
|
||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._native.register();
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
let requestId;
|
||||
|
||||
for (requestId in this._dialogs)
|
||||
this._dialogs[requestId].cancel();
|
||||
this._dialogs = { };
|
||||
|
||||
for (requestId in this._vpnRequests)
|
||||
this._vpnRequests[requestId].cancel(true);
|
||||
this._vpnRequests = { };
|
||||
|
||||
this._native.unregister();
|
||||
},
|
||||
|
||||
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
||||
if (settingName == 'vpn') {
|
||||
this._vpnRequest(requestId, connection, hints, flags);
|
||||
@ -633,7 +635,7 @@ const NetworkAgent = new Lang.Class({
|
||||
this._dialogs[requestId].destroy();
|
||||
delete this._dialogs[requestId];
|
||||
} else if (this._vpnRequests[requestId]) {
|
||||
this._vpnRequests[requestId].cancel();
|
||||
this._vpnRequests[requestId].cancel(false);
|
||||
delete this._vpnRequests[requestId];
|
||||
}
|
||||
},
|
||||
@ -702,3 +704,4 @@ const NetworkAgent = new Lang.Class({
|
||||
}
|
||||
}
|
||||
});
|
||||
const Component = NetworkAgent;
|
@ -1,24 +1,4 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
/*
|
||||
* Copyright 2010 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
@ -33,6 +13,7 @@ const Mainloop = imports.mainloop;
|
||||
const Polkit = imports.gi.Polkit;
|
||||
const PolkitAgent = imports.gi.PolkitAgent;
|
||||
|
||||
const Components = imports.ui.components;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
@ -318,7 +299,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onUserChanged: function() {
|
||||
if (this._user.is_loaded) {
|
||||
if (this._user.is_loaded && this._userAvatar) {
|
||||
this._userAvatar.update();
|
||||
this._userAvatar.actor.show();
|
||||
}
|
||||
@ -336,11 +317,20 @@ const AuthenticationAgent = new Lang.Class({
|
||||
Name: 'AuthenticationAgent',
|
||||
|
||||
_init: function() {
|
||||
this._currentDialog = null;
|
||||
this._isCompleting = false;
|
||||
this._handle = null;
|
||||
this._native = new Shell.PolkitAuthenticationAgent();
|
||||
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
||||
this._currentDialog = null;
|
||||
this._isCompleting = false;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._native.register();
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this._native.unregister();
|
||||
},
|
||||
|
||||
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||
@ -398,6 +388,4 @@ const AuthenticationAgent = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
function init() {
|
||||
let agent = new AuthenticationAgent();
|
||||
}
|
||||
const Component = AuthenticationAgent;
|
58
js/ui/components/recorder.js
Normal file
58
js/ui/components/recorder.js
Normal file
@ -0,0 +1,58 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Recorder = new Lang.Class({
|
||||
Name: 'Recorder',
|
||||
|
||||
_init: function() {
|
||||
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
||||
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
||||
this._recorder = null;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
global.display.add_keybinding('toggle-recording',
|
||||
this._bindingSettings,
|
||||
Meta.KeyBindingFlags.NONE, Lang.bind(this, this._toggleRecorder));
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
global.display.remove_keybinding('toggle-recording');
|
||||
},
|
||||
|
||||
_ensureRecorder: function() {
|
||||
if (this._recorder == null)
|
||||
this._recorder = new Shell.Recorder({ stage: global.stage });
|
||||
return this._recorder;
|
||||
},
|
||||
|
||||
_toggleRecorder: function() {
|
||||
let recorder = this._ensureRecorder();
|
||||
if (recorder.is_recording()) {
|
||||
recorder.close();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
} else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(this._recorderSettings.get_int('framerate'));
|
||||
/* Translators: this is a filename used for screencast recording */
|
||||
// xgettext:no-c-format
|
||||
recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
|
||||
let pipeline = this._recorderSettings.get_string('pipeline');
|
||||
|
||||
if (!pipeline.match(/^\s*$/))
|
||||
recorder.set_pipeline(pipeline);
|
||||
else
|
||||
recorder.set_pipeline(null);
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
recorder.record();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const Component = Recorder;
|
@ -11,10 +11,10 @@ const Tpl = imports.gi.TelepathyLogger;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
|
||||
const History = imports.misc.history;
|
||||
const Params = imports.misc.params;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
// See Notification.appendMessage
|
||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
||||
@ -63,10 +63,10 @@ function makeMessageFromTplEvent(event) {
|
||||
};
|
||||
}
|
||||
|
||||
const Client = new Lang.Class({
|
||||
Name: 'Client',
|
||||
const TelepathyClient = new Lang.Class({
|
||||
Name: 'TelepathyClient',
|
||||
|
||||
_init : function() {
|
||||
_init: function() {
|
||||
// channel path -> ChatSource
|
||||
this._chatSources = {};
|
||||
this._chatState = Tp.ChannelChatState.ACTIVE;
|
||||
@ -89,9 +89,9 @@ const Client = new Lang.Class({
|
||||
// channel matching its filters is detected.
|
||||
// The second argument, recover, means _observeChannels will be run
|
||||
// for any existing channel as well.
|
||||
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
|
||||
'name': 'GnomeShell',
|
||||
'uniquify-name': true })
|
||||
this._tpClient = new Shell.TpClient({ name: 'GnomeShell',
|
||||
account_manager: this._accountManager,
|
||||
uniquify_name: true });
|
||||
this._tpClient.set_observe_channels_func(
|
||||
Lang.bind(this, this._observeChannels));
|
||||
this._tpClient.set_approve_channels_func(
|
||||
@ -99,6 +99,10 @@ const Client = new Lang.Class({
|
||||
this._tpClient.set_handle_channels_func(
|
||||
Lang.bind(this, this._handleChannels));
|
||||
|
||||
// Watch subscription requests and connection errors
|
||||
this._subscriptionSource = null;
|
||||
this._accountSource = null;
|
||||
|
||||
// Workaround for gjs not supporting GPtrArray in signals.
|
||||
// See BGO bug #653941 for context.
|
||||
this._tpClient.set_contact_list_changed_func(
|
||||
@ -108,21 +112,26 @@ const Client = new Lang.Class({
|
||||
// needed
|
||||
this._tpClient.set_delegated_channels_callback(
|
||||
Lang.bind(this, this._delegatedChannelsCb));
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
try {
|
||||
this._tpClient.register();
|
||||
} catch (e) {
|
||||
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
|
||||
}
|
||||
|
||||
// Watch subscription requests and connection errors
|
||||
this._subscriptionSource = null;
|
||||
this._accountSource = null;
|
||||
this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
|
||||
Lang.bind(this, this._accountValidityChanged));
|
||||
|
||||
this._accountManager.connect('account-validity-changed',
|
||||
Lang.bind(this, this._accountValidityChanged));
|
||||
if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
|
||||
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
|
||||
},
|
||||
|
||||
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
|
||||
disable: function() {
|
||||
this._tpClient.unregister();
|
||||
this._accountManager.disconnect(this._accountManagerValidityChangedId);
|
||||
this._accountManagerValidityChangedId = 0;
|
||||
},
|
||||
|
||||
_observeChannels: function(observer, account, conn, channels,
|
||||
@ -473,6 +482,22 @@ const ChatSource = new Lang.Class({
|
||||
this._getLogMessages();
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
let item;
|
||||
|
||||
let rightClickMenu = this.parent();
|
||||
item = new PopupMenu.PopupMenuItem('');
|
||||
item.actor.connect('notify::mapped', Lang.bind(this, function() {
|
||||
item.label.set_text(this.isMuted ? _("Unmute") : _("Mute"));
|
||||
}));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this.setMuted(!this.isMuted);
|
||||
this.emit('done-displaying-content');
|
||||
}));
|
||||
rightClickMenu.add(item.actor);
|
||||
return rightClickMenu;
|
||||
},
|
||||
|
||||
_updateAlias: function() {
|
||||
let oldAlias = this.title;
|
||||
let newAlias = this._contact.get_alias();
|
||||
@ -484,51 +509,42 @@ const ChatSource = new Lang.Class({
|
||||
this._notification.appendAliasChange(oldAlias, newAlias);
|
||||
},
|
||||
|
||||
createIcon: function(size) {
|
||||
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
|
||||
this._iconBox._size = size;
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
getIcon: function() {
|
||||
let file = this._contact.get_avatar_file();
|
||||
|
||||
if (file) {
|
||||
let uri = file.get_uri();
|
||||
this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size);
|
||||
return new Gio.FileIcon({ file: file });
|
||||
} else {
|
||||
this._iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||
icon_size: this._iconBox._size });
|
||||
return new Gio.ThemedIcon({ name: 'avatar-default' });
|
||||
}
|
||||
|
||||
return this._iconBox;
|
||||
},
|
||||
|
||||
createSecondaryIcon: function() {
|
||||
let iconBox = new St.Bin();
|
||||
iconBox.child = new St.Icon({ style_class: 'secondary-icon' });
|
||||
getSecondaryIcon: function() {
|
||||
let iconName;
|
||||
let presenceType = this._contact.get_presence_type();
|
||||
|
||||
switch (presenceType) {
|
||||
case Tp.ConnectionPresenceType.AVAILABLE:
|
||||
iconBox.child.icon_name = 'user-available';
|
||||
iconName = 'user-available';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.BUSY:
|
||||
iconBox.child.icon_name = 'user-busy';
|
||||
iconName = 'user-busy';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.OFFLINE:
|
||||
iconBox.child.icon_name = 'user-offline';
|
||||
iconName = 'user-offline';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.HIDDEN:
|
||||
iconBox.child.icon_name = 'user-invisible';
|
||||
iconName = 'user-invisible';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.AWAY:
|
||||
iconBox.child.icon_name = 'user-away';
|
||||
iconName = 'user-away';
|
||||
break;
|
||||
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
|
||||
iconBox.child.icon_name = 'user-idle';
|
||||
iconName = 'user-idle';
|
||||
break;
|
||||
default:
|
||||
iconBox.child.icon_name = 'user-offline';
|
||||
iconName = 'user-offline';
|
||||
}
|
||||
return iconBox;
|
||||
return new Gio.ThemedIcon({ name: iconName });
|
||||
},
|
||||
|
||||
_updateAvatarIcon: function() {
|
||||
@ -539,7 +555,9 @@ const ChatSource = new Lang.Class({
|
||||
open: function(notification) {
|
||||
if (this._client.is_handling_channel(this._channel)) {
|
||||
// We are handling the channel, try to pass it to Empathy
|
||||
this._client.delegate_channels_async([this._channel], global.get_current_time(), '', null);
|
||||
this._client.delegate_channels_async([this._channel],
|
||||
global.get_current_time(),
|
||||
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
|
||||
}
|
||||
else {
|
||||
// We are not the handler, just ask to present the channel
|
||||
@ -708,7 +726,7 @@ const ChatSource = new Lang.Class({
|
||||
|
||||
title = GLib.markup_escape_text(this.title, -1);
|
||||
|
||||
this._notification.update(this._notification.title, null, { customContent: true, secondaryIcon: this.createSecondaryIcon() });
|
||||
this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() });
|
||||
|
||||
if (message)
|
||||
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
|
||||
@ -721,8 +739,6 @@ const ChatSource = new Lang.Class({
|
||||
this._pendingMessages.splice(idx, 1);
|
||||
this.countUpdated();
|
||||
}
|
||||
else
|
||||
throw new Error('Message not in our pending list: ' + message);
|
||||
},
|
||||
|
||||
_ackMessages: function() {
|
||||
@ -738,7 +754,7 @@ const ChatNotification = new Lang.Class({
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source) {
|
||||
this.parent(source, source.title, null, { customContent: true, secondaryIcon: source.createSecondaryIcon() });
|
||||
this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() });
|
||||
this.setResident(true);
|
||||
|
||||
this._responseEntry = new St.Entry({ style_class: 'chat-response',
|
||||
@ -747,6 +763,14 @@ const ChatNotification = new Lang.Class({
|
||||
this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
|
||||
this.setActionArea(this._responseEntry);
|
||||
|
||||
this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() {
|
||||
this.focused = true;
|
||||
}));
|
||||
this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() {
|
||||
this.focused = false;
|
||||
this.emit('unfocused');
|
||||
}));
|
||||
|
||||
this._oldMaxScrollAdjustment = 0;
|
||||
this._createScrollArea();
|
||||
this._lastGroup = null;
|
||||
@ -908,24 +932,32 @@ const ChatNotification = new Lang.Class({
|
||||
|
||||
let format;
|
||||
|
||||
// Show a week day and time if date is in the last week
|
||||
if (daysAgo < 1 || (daysAgo < 7 && now.getDay() != date.getDay())) {
|
||||
/* Translators: this is a time format string followed by a date.
|
||||
If applicable, replace %X with a strftime format valid for your
|
||||
locale, without seconds. */
|
||||
// Show only the hour if date is on today
|
||||
if(daysAgo < 1){
|
||||
format = "<b>%H:%M</b>";
|
||||
}
|
||||
// Show the word "Yesterday" and time if date is on yesterday
|
||||
else if(daysAgo <2){
|
||||
/* Translators: this is a time format string followed by the word "Yesterday". i.e. "14:30 on Yesterday"*/
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent at <b>%X</b> on <b>%A</b>");
|
||||
format = _("<b>%H:%M</b> on Yesterday");
|
||||
}
|
||||
// Show a week day and time if date is in the last week
|
||||
else if (daysAgo < 7) {
|
||||
/* Translators: this is a time format string followed by a week day name. i.e. "14:30 on Monday*/
|
||||
// xgettext:no-c-format
|
||||
format = _("<b>%H:%M</b> on <b>%A</b>");
|
||||
|
||||
} else if (date.getYear() == now.getYear()) {
|
||||
/* Translators: this is a time format in the style of "Wednesday, May 25",
|
||||
shown when you get a chat message in the same year. */
|
||||
/* Translators: this is a time format in the style of "14:30 on Wednesday, May 25",
|
||||
shown when you get a chat message in the same year */
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent on <b>%A</b>, <b>%B %d</b>");
|
||||
format = _("<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>");
|
||||
} else {
|
||||
/* Translators: this is a time format in the style of "Wednesday, May 25, 2012",
|
||||
shown when you get a chat message in a different year. */
|
||||
/* Translators: this is a time format in the style of "14:30 on Wednesday, May 25, 2012",
|
||||
shown when you get a chat message in a different year */
|
||||
// xgettext:no-c-format
|
||||
format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y");
|
||||
format = _("<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y");
|
||||
}
|
||||
|
||||
return date.toLocaleFormat(format);
|
||||
@ -938,8 +970,7 @@ const ChatNotification = new Lang.Class({
|
||||
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'],
|
||||
childProps: { expand: true, x_fill: false,
|
||||
x_align: St.Align.END },
|
||||
childProps: { expand: true, x_fill: false },
|
||||
noTimestamp: true,
|
||||
timestamp: lastMessageTime });
|
||||
|
||||
@ -1040,9 +1071,8 @@ const ApproverSource = new Lang.Class({
|
||||
this.parent();
|
||||
},
|
||||
|
||||
createIcon: function(size) {
|
||||
return new St.Icon({ gicon: this._gicon,
|
||||
icon_size: size });
|
||||
getIcon: function() {
|
||||
return this._gicon;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1399,3 +1429,4 @@ const AccountNotification = new Lang.Class({
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
const Component = TelepathyClient;
|
@ -54,16 +54,17 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
},
|
||||
|
||||
focusGroup: function(item) {
|
||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
|
||||
if (item.window)
|
||||
if (item.window) {
|
||||
Main.activateWindow(item.window);
|
||||
else if (item.focusCallback)
|
||||
} else if (item.focusCallback) {
|
||||
item.focusCallback();
|
||||
else
|
||||
} else {
|
||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
|
||||
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
}
|
||||
},
|
||||
|
||||
// Sort the items into a consistent order; panel first, tray last,
|
||||
|
206
js/ui/dash.js
206
js/ui/dash.js
@ -21,6 +21,18 @@ const DASH_ITEM_LABEL_SHOW_TIME = 0.15;
|
||||
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
|
||||
const DASH_ITEM_HOVER_TIMEOUT = 300;
|
||||
|
||||
function getAppFromSource(source) {
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
return appSystem.lookup_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
return tracker.get_window_app(source.metaWindow);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// A container like StBin, but taking the child's scale into account
|
||||
// when requesting a size
|
||||
const DashItemContainer = new Lang.Class({
|
||||
@ -36,7 +48,11 @@ const DashItemContainer = new Lang.Class({
|
||||
Lang.bind(this, this._allocate));
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.label = null;
|
||||
this._labelText = "";
|
||||
this.label = new St.Label({ style_class: 'dash-label'});
|
||||
this.label.hide();
|
||||
Main.layoutManager.addChrome(this.label);
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this.child = null;
|
||||
this._childScale = 1;
|
||||
@ -91,9 +107,10 @@ const DashItemContainer = new Lang.Class({
|
||||
},
|
||||
|
||||
showLabel: function() {
|
||||
if (this.label == null)
|
||||
if (!this._labelText)
|
||||
return;
|
||||
|
||||
this.label.set_text(this._labelText);
|
||||
this.label.opacity = 0;
|
||||
this.label.show();
|
||||
|
||||
@ -124,19 +141,10 @@ const DashItemContainer = new Lang.Class({
|
||||
},
|
||||
|
||||
setLabelText: function(text) {
|
||||
if (this.label == null) {
|
||||
this.label = new St.Label({ style_class: 'dash-label'});
|
||||
Main.layoutManager.addChrome(this.label);
|
||||
this.label.hide();
|
||||
}
|
||||
|
||||
this.label.set_text(text);
|
||||
this._labelText = text;
|
||||
},
|
||||
|
||||
hideLabel: function () {
|
||||
if (this.label == null)
|
||||
return;
|
||||
|
||||
Tweener.addTween(this.label,
|
||||
{ opacity: 0,
|
||||
time: DASH_ITEM_LABEL_HIDE_TIME,
|
||||
@ -250,7 +258,7 @@ const ShowAppsIcon = new Lang.Class({
|
||||
this.toggleButton._delegate = this;
|
||||
|
||||
this.setChild(this.toggleButton);
|
||||
this.setHover(false);
|
||||
this.setDragApp(null);
|
||||
this.toggleButton.label_actor = this.label;
|
||||
},
|
||||
|
||||
@ -262,31 +270,45 @@ const ShowAppsIcon = new Lang.Class({
|
||||
return this._iconActor;
|
||||
},
|
||||
|
||||
setHover: function(hovered) {
|
||||
this.toggleButton.set_hover(hovered);
|
||||
if (this._iconActor)
|
||||
this._iconActor.set_hover(hovered);
|
||||
_canRemoveApp: function(app) {
|
||||
if (app == null)
|
||||
return false;
|
||||
|
||||
if (hovered)
|
||||
let id = app.get_id();
|
||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
|
||||
return isFavorite;
|
||||
},
|
||||
|
||||
setDragApp: function(app) {
|
||||
let canRemove = this._canRemoveApp(app);
|
||||
|
||||
this.toggleButton.set_hover(canRemove);
|
||||
if (this._iconActor)
|
||||
this._iconActor.set_hover(canRemove);
|
||||
|
||||
if (canRemove)
|
||||
this.setLabelText(_("Remove from Favorites"));
|
||||
else
|
||||
this.setLabelText(_("Show Applications"));
|
||||
},
|
||||
|
||||
// Rely on the dragged item being a favorite
|
||||
handleDragOver: function(source, actor, x, y, time) {
|
||||
let app = getAppFromSource(source);
|
||||
if (app == null)
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
let id = app.get_id();
|
||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
|
||||
if (!isFavorite)
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
return DND.DragMotionResult.MOVE_DROP;
|
||||
},
|
||||
|
||||
acceptDrop: function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
app = appSystem.lookup_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
app = tracker.get_window_app(source.metaWindow);
|
||||
}
|
||||
let app = getAppFromSource(source);
|
||||
if (app == null)
|
||||
return false;
|
||||
|
||||
let id = app.get_id();
|
||||
|
||||
@ -310,6 +332,39 @@ const DragPlaceholderItem = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const DashActor = new Lang.Class({
|
||||
Name: 'DashActor',
|
||||
Extends: St.Widget,
|
||||
|
||||
_init: function() {
|
||||
let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||
this.parent({ name: 'dash',
|
||||
layout_manager: layout,
|
||||
clip_to_allocation: true });
|
||||
},
|
||||
|
||||
vfunc_allocate: function(box, flags) {
|
||||
let contentBox = this.get_theme_node().get_content_box(box);
|
||||
let availWidth = contentBox.x2 - contentBox.x1;
|
||||
|
||||
this.set_allocation(box, flags);
|
||||
|
||||
let [appIcons, showAppsButton] = this.get_children();
|
||||
let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth);
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = contentBox.x1;
|
||||
childBox.y1 = contentBox.y1;
|
||||
childBox.x2 = contentBox.x2;
|
||||
childBox.y2 = contentBox.y2 - showAppsNatHeight;
|
||||
appIcons.allocate(childBox, flags);
|
||||
|
||||
childBox.y1 = contentBox.y2 - showAppsNatHeight;
|
||||
childBox.y2 = contentBox.y2;
|
||||
showAppsButton.allocate(childBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const Dash = new Lang.Class({
|
||||
Name: 'Dash',
|
||||
|
||||
@ -317,6 +372,9 @@ const Dash = new Lang.Class({
|
||||
this._maxHeight = -1;
|
||||
this.iconSize = 64;
|
||||
this._shownInitially = false;
|
||||
this.visible = false;
|
||||
this._hiddenX;
|
||||
this._targetX;
|
||||
|
||||
this._dragPlaceholder = null;
|
||||
this._dragPlaceholderPos = -1;
|
||||
@ -325,14 +383,11 @@ const Dash = new Lang.Class({
|
||||
this._resetHoverTimeoutId = 0;
|
||||
this._labelShowing = false;
|
||||
|
||||
this._container = new St.BoxLayout({ name: 'dash',
|
||||
vertical: true,
|
||||
clip_to_allocation: true });
|
||||
|
||||
this._container = new DashActor();
|
||||
this._box = new St.BoxLayout({ vertical: true,
|
||||
clip_to_allocation: true });
|
||||
this._box._delegate = this;
|
||||
this._container.add(this._box);
|
||||
this._container.add_actor(this._box);
|
||||
|
||||
this._showAppsIcon = new ShowAppsIcon();
|
||||
this._showAppsIcon.icon.setIconSize(this.iconSize);
|
||||
@ -340,30 +395,30 @@ const Dash = new Lang.Class({
|
||||
|
||||
this.showAppsButton = this._showAppsIcon.toggleButton;
|
||||
|
||||
this._container.add(this._showAppsIcon.actor);
|
||||
this._container.add_actor(this._showAppsIcon.actor);
|
||||
|
||||
this.actor = new St.Bin({ child: this._container });
|
||||
this.actor.connect('notify::height', Lang.bind(this,
|
||||
function() {
|
||||
if (this._maxHeight != this.actor.height)
|
||||
this._queueRedisplay();
|
||||
this._maxHeight = this.actor.height;
|
||||
if (this.actor.height > this._maxHeight)
|
||||
this._maxHeight = this.actor.height;
|
||||
}));
|
||||
|
||||
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
|
||||
|
||||
this._tracker = Shell.WindowTracker.get_default();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
||||
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
||||
this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Main.overview.connect('app-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
Main.overview.connect('item-drag-end',
|
||||
Main.overview.connect('app-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
Main.overview.connect('item-drag-cancelled',
|
||||
Main.overview.connect('app-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
@ -395,31 +450,25 @@ const Dash = new Lang.Class({
|
||||
|
||||
_endDrag: function() {
|
||||
this._clearDragPlaceholder();
|
||||
this._showAppsIcon.setDragApp(null);
|
||||
DND.removeDragMonitor(this._dragMonitor);
|
||||
},
|
||||
|
||||
_onDragMotion: function(dragEvent) {
|
||||
let app = null;
|
||||
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
|
||||
app = this._appSystem.lookup_app(dragEvent.source.getId());
|
||||
else if (dragEvent.source.metaWindow)
|
||||
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
|
||||
else
|
||||
let app = getAppFromSource(dragEvent.source);
|
||||
if (app == null)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
let id = app.get_id();
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||
|
||||
let srcIsFavorite = (id in favorites);
|
||||
|
||||
let showAppsHovered =
|
||||
this._showAppsIcon.actor.contains(dragEvent.targetActor);
|
||||
|
||||
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
|
||||
this._clearDragPlaceholder();
|
||||
|
||||
this._showAppsIcon.setHover(showAppsHovered);
|
||||
if (showAppsHovered)
|
||||
this._showAppsIcon.setDragApp(app);
|
||||
else
|
||||
this._showAppsIcon.setDragApp(null);
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
@ -501,6 +550,17 @@ const Dash = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_computeDashX: function() {
|
||||
this._targetX = this.actor.get_x();
|
||||
|
||||
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
if (rtl)
|
||||
this._hiddenX = this._targetX + this.actor.width;
|
||||
else
|
||||
this._hiddenX = -this.actor.width;
|
||||
},
|
||||
|
||||
_adjustIconSize: function() {
|
||||
// For the icon size, we only consider children which are "proper"
|
||||
// icons (i.e. ignoring drag placeholders) and which are not
|
||||
@ -596,6 +656,8 @@ const Dash = new Lang.Class({
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._computeDashX();
|
||||
},
|
||||
|
||||
_redisplay: function () {
|
||||
@ -712,6 +774,7 @@ const Dash = new Lang.Class({
|
||||
// of items, to avoid all items zooming in at once
|
||||
if (!this._shownInitially) {
|
||||
this._shownInitially = true;
|
||||
this.visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -732,11 +795,7 @@ const Dash = new Lang.Class({
|
||||
},
|
||||
|
||||
handleDragOver : function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon)
|
||||
app = this._appSystem.lookup_app(source.getId());
|
||||
else if (source.metaWindow)
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
let app = getAppFromSource(source);
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_window_backed())
|
||||
@ -817,12 +876,7 @@ const Dash = new Lang.Class({
|
||||
|
||||
// Draggable target interface
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
app = this._appSystem.lookup_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
}
|
||||
let app = getAppFromSource(source);
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_window_backed()) {
|
||||
@ -865,6 +919,34 @@ const Dash = new Lang.Class({
|
||||
}));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (this.visible)
|
||||
return;
|
||||
|
||||
this.visible = true;
|
||||
|
||||
this.actor.show();
|
||||
Tweener.addTween(this.actor, { translation_x: this._targetX,
|
||||
transition: 'easeOutQuad',
|
||||
time: DASH_ANIMATION_TIME
|
||||
});
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (!this.visible)
|
||||
return;
|
||||
|
||||
this.visible = false;
|
||||
|
||||
Tweener.addTween(this.actor, { translation_x: this._hiddenX,
|
||||
transition: 'easeOutQuad',
|
||||
time: DASH_ANIMATION_TIME,
|
||||
onComplete: Lang.bind(this, function () {
|
||||
this.actor.hide();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -70,16 +70,8 @@ const DateMenuButton = new Lang.Class({
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
vbox.add(this._date);
|
||||
|
||||
if (Main.sessionMode.showCalendarEvents) {
|
||||
this._eventSource = new Calendar.DBusEventSource();
|
||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
||||
} else {
|
||||
this._eventSource = null;
|
||||
this._eventList = null;
|
||||
}
|
||||
|
||||
// Calendar
|
||||
this._calendar = new Calendar.Calendar(this._eventSource);
|
||||
this._eventList = new Calendar.EventsList();
|
||||
this._calendar = new Calendar.Calendar();
|
||||
|
||||
this._calendar.connect('selected-date-changed',
|
||||
Lang.bind(this, function(calendar, date) {
|
||||
@ -97,31 +89,34 @@ const DateMenuButton = new Lang.Class({
|
||||
separator.setColumnWidths(1);
|
||||
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
item.actor.show_on_set_parent = false;
|
||||
item.actor.can_focus = false;
|
||||
item.actor.reparent(vbox);
|
||||
this._dateAndTimeSeparator = separator;
|
||||
}
|
||||
|
||||
if (Main.sessionMode.showCalendarEvents) {
|
||||
// Add vertical separator
|
||||
this._separator = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
this._separator.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(this._separator);
|
||||
|
||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(item);
|
||||
// Fill up the second column
|
||||
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
|
||||
vertical: true });
|
||||
hbox.add(vbox, { expand: true });
|
||||
|
||||
// Fill up the second column
|
||||
vbox = new St.BoxLayout({name: 'calendarEventsArea',
|
||||
vertical: true});
|
||||
hbox.add(vbox, { expand: true });
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
this._openCalendarItem.actor.can_focus = false;
|
||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
}
|
||||
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
this._calendarSettings.connect('changed::exec',
|
||||
Lang.bind(this, this._calendarSettingsChanged));
|
||||
this._calendarSettingsChanged();
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
@ -151,6 +146,51 @@ const DateMenuButton = new Lang.Class({
|
||||
this._clock = new GnomeDesktop.WallClock();
|
||||
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
|
||||
this._updateClockAndDate();
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_calendarSettingsChanged: function() {
|
||||
let exec = this._calendarSettings.get_string('exec');
|
||||
let fullExec = GLib.find_program_in_path(exec);
|
||||
this._openCalendarItem.actor.visible = fullExec != null;
|
||||
},
|
||||
|
||||
_setEventsVisibility: function(visible) {
|
||||
this._openCalendarItem.actor.visible = visible;
|
||||
this._separator.visible = visible;
|
||||
if (visible) {
|
||||
let alignment = 0.25;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
alignment = 1.0 - alignment;
|
||||
this.menu._arrowAlignment = alignment;
|
||||
this._eventList.actor.get_parent().show();
|
||||
} else {
|
||||
this.menu._arrowAlignment = 0.5;
|
||||
this._eventList.actor.get_parent().hide();
|
||||
}
|
||||
},
|
||||
|
||||
_setEventSource: function(eventSource) {
|
||||
this._calendar.setEventSource(eventSource);
|
||||
this._eventList.setEventSource(eventSource);
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
let eventSource;
|
||||
let showEvents = Main.sessionMode.showCalendarEvents;
|
||||
if (showEvents) {
|
||||
eventSource = new Calendar.DBusEventSource();
|
||||
} else {
|
||||
eventSource = null;
|
||||
}
|
||||
this._setEventSource(eventSource);
|
||||
this._setEventsVisibility(showEvents);
|
||||
|
||||
// This needs to be handled manually, as the code to
|
||||
// autohide separators doesn't work across the vbox
|
||||
this._dateAndTimeSeparator.actor.visible = Main.sessionMode.allowSettings;
|
||||
},
|
||||
|
||||
_updateClockAndDate: function() {
|
||||
@ -165,14 +205,13 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
let tool = calendarSettings.get_string('exec');
|
||||
let tool = this._calendarSettings.get_string('exec');
|
||||
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
||||
// TODO: pass the selected day
|
||||
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
||||
app.activate();
|
||||
} else {
|
||||
let needTerm = calendarSettings.get_boolean('needs-term');
|
||||
let needTerm = this._calendarSettings.get_boolean('needs-term');
|
||||
if (needTerm) {
|
||||
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
||||
let term = terminalSettings.get_string('exec');
|
||||
|
13
js/ui/dnd.js
13
js/ui/dnd.js
@ -235,6 +235,10 @@ const _Draggable = new Lang.Class({
|
||||
|
||||
if (this.actor._delegate && this.actor._delegate.getDragActor) {
|
||||
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
this._dragActor.raise_top();
|
||||
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
|
||||
// can be an image that's part of the actor. So to perform "snap back" correctly we need
|
||||
// to know what was the drag actor source.
|
||||
@ -263,12 +267,17 @@ const _Draggable = new Lang.Class({
|
||||
this._dragOffsetY = this._dragActor.y - this._dragStartY;
|
||||
} else {
|
||||
this._dragActor = this.actor;
|
||||
|
||||
this._dragActorSource = undefined;
|
||||
this._dragOrigParent = this.actor.get_parent();
|
||||
this._dragOrigX = this._dragActor.x;
|
||||
this._dragOrigY = this._dragActor.y;
|
||||
this._dragOrigScale = this._dragActor.scale_x;
|
||||
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
this._dragActor.raise_top();
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||
|
||||
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
|
||||
this._dragOffsetX = actorStageX - this._dragStartX;
|
||||
this._dragOffsetY = actorStageY - this._dragStartY;
|
||||
@ -280,10 +289,6 @@ const _Draggable = new Lang.Class({
|
||||
scaledHeight / this.actor.height);
|
||||
}
|
||||
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
this._dragActor.raise_top();
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||
|
||||
this._dragOrigOpacity = this._dragActor.opacity;
|
||||
if (this._dragActorOpacity != undefined)
|
||||
this._dragActor.opacity = this._dragActorOpacity;
|
||||
|
@ -8,6 +8,7 @@ const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ExtensionState = {
|
||||
ENABLED: 1,
|
||||
@ -38,6 +39,9 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||
|
||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||
|
||||
var initted = false;
|
||||
var enabled;
|
||||
|
||||
function disableExtension(uuid) {
|
||||
let extension = ExtensionUtils.extensions[uuid];
|
||||
if (!extension)
|
||||
@ -102,8 +106,6 @@ function enableExtension(uuid) {
|
||||
|
||||
extensionOrder.push(uuid);
|
||||
|
||||
extension.stateObj.enable();
|
||||
|
||||
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
@ -111,6 +113,8 @@ function enableExtension(uuid) {
|
||||
extension.stylesheet = stylesheetFile;
|
||||
}
|
||||
|
||||
extension.stateObj.enable();
|
||||
|
||||
extension.state = ExtensionState.ENABLED;
|
||||
_signals.emit('extension-state-changed', extension);
|
||||
}
|
||||
@ -216,6 +220,9 @@ function initExtension(uuid) {
|
||||
function onEnabledExtensionsChanged() {
|
||||
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
// Find and enable all the newly enabled extensions: UUIDs found in the
|
||||
// new setting, but not in the old one.
|
||||
newEnabledExtensions.filter(function(uuid) {
|
||||
@ -243,7 +250,7 @@ function onEnabledExtensionsChanged() {
|
||||
enabledExtensions = newEnabledExtensions;
|
||||
}
|
||||
|
||||
function loadExtensions() {
|
||||
function _loadExtensions() {
|
||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
|
||||
|
||||
@ -257,3 +264,43 @@ function loadExtensions() {
|
||||
});
|
||||
finder.scanExtensions();
|
||||
}
|
||||
|
||||
function enableAllExtensions() {
|
||||
if (enabled)
|
||||
return;
|
||||
|
||||
if (!initted) {
|
||||
_loadExtensions();
|
||||
initted = true;
|
||||
} else {
|
||||
enabledExtensions.forEach(function(uuid) {
|
||||
enableExtension(uuid);
|
||||
});
|
||||
}
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
function disableAllExtensions() {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
if (initted) {
|
||||
enabledExtensions.forEach(function(uuid) {
|
||||
disableExtension(uuid);
|
||||
});
|
||||
}
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
function _sessionUpdated() {
|
||||
if (Main.sessionMode.allowExtensions)
|
||||
enableAllExtensions();
|
||||
else
|
||||
disableAllExtensions();
|
||||
}
|
||||
|
||||
function init() {
|
||||
Main.sessionMode.connect('updated', _sessionUpdated);
|
||||
_sessionUpdated();
|
||||
}
|
||||
|
@ -10,11 +10,12 @@ const St = imports.gi.St;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
function _navigateActor(actor, needsGrab) {
|
||||
function _navigateActor(actor) {
|
||||
if (!actor)
|
||||
return;
|
||||
|
||||
if (needsGrab && actor instanceof St.Widget)
|
||||
let needsGrab = true;
|
||||
if (actor instanceof St.Widget)
|
||||
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
if (needsGrab)
|
||||
actor.grab_key_focus();
|
||||
@ -84,11 +85,7 @@ const GrabHelper = new Lang.Class({
|
||||
},
|
||||
|
||||
get currentGrab() {
|
||||
let idx = this._grabStack.length - 1;
|
||||
while (idx >= 0 && this._grabStack[idx].untracked)
|
||||
idx--;
|
||||
|
||||
return this._grabStack[idx] || {};
|
||||
return this._grabStack[this._grabStack.length - 1] || {};
|
||||
},
|
||||
|
||||
_findStackIndex: function(actor) {
|
||||
@ -137,14 +134,9 @@ const GrabHelper = new Lang.Class({
|
||||
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
|
||||
// revert it back, and re-focus the previously-focused window (if
|
||||
// another window hasn't been explicitly focused before then).
|
||||
//
|
||||
// If @params contains { untracked: true }, then it will be skipped
|
||||
// when the grab helper ungrabs for you, or when calculating
|
||||
// currentGrab.
|
||||
grab: function(params) {
|
||||
params = Params.parse(params, { actor: null,
|
||||
modal: false,
|
||||
untracked: false,
|
||||
grabFocus: false,
|
||||
onUngrab: null });
|
||||
|
||||
@ -155,55 +147,109 @@ const GrabHelper = new Lang.Class({
|
||||
if (this.isActorGrabbed(params.actor))
|
||||
return true;
|
||||
|
||||
if (this._grabFocusCount == 0 && this._modalCount == 0) {
|
||||
if (!this._fullGrab(hadFocus, params.modal, params.grabFocus))
|
||||
return false;
|
||||
}
|
||||
|
||||
params.savedFocus = focus;
|
||||
|
||||
if (params.modal && !this._takeModalGrab())
|
||||
return false;
|
||||
|
||||
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
|
||||
return false;
|
||||
|
||||
if (hadFocus || params.grabFocus)
|
||||
_navigateActor(newFocus);
|
||||
|
||||
this._grabStack.push(params);
|
||||
|
||||
if (params.modal)
|
||||
this._modalCount++;
|
||||
|
||||
if (params.grabFocus)
|
||||
this._grabFocusCount++;
|
||||
|
||||
_navigateActor(newFocus, hadFocus);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_fullGrab: function(hadFocus, modal, grabFocus) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
|
||||
if (modal) {
|
||||
_takeModalGrab: function() {
|
||||
let firstGrab = (this._modalCount == 0);
|
||||
if (firstGrab) {
|
||||
if (!Main.pushModal(this._owner))
|
||||
return false;
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
|
||||
}
|
||||
|
||||
this._grabbedFromKeynav = hadFocus;
|
||||
this._preGrabInputMode = global.stage_input_mode;
|
||||
this._prevFocusedWindow = null;
|
||||
this._modalCount++;
|
||||
return true;
|
||||
},
|
||||
|
||||
if (grabFocus) {
|
||||
_releaseModalGrab: function() {
|
||||
this._modalCount--;
|
||||
if (this._modalCount > 0)
|
||||
return;
|
||||
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
if (this._eventId > 0) {
|
||||
global.stage.disconnect(this._eventId);
|
||||
this._eventId = 0;
|
||||
}
|
||||
|
||||
Main.popModal(this._owner);
|
||||
global.sync_pointer();
|
||||
},
|
||||
|
||||
_takeFocusGrab: function(hadFocus) {
|
||||
let firstGrab = (this._grabFocusCount == 0);
|
||||
if (firstGrab) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
|
||||
this._grabbedFromKeynav = hadFocus;
|
||||
this._preGrabInputMode = global.stage_input_mode;
|
||||
this._prevFocusedWindow = metaDisplay.focus_window;
|
||||
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
|
||||
this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
}
|
||||
}
|
||||
|
||||
if (modal || grabFocus) {
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
|
||||
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
|
||||
}
|
||||
|
||||
this._grabFocusCount++;
|
||||
return true;
|
||||
},
|
||||
|
||||
_releaseFocusGrab: function() {
|
||||
this._grabFocusCount--;
|
||||
if (this._grabFocusCount > 0)
|
||||
return;
|
||||
|
||||
if (this._keyFocusNotifyId > 0) {
|
||||
global.stage.disconnect(this._keyFocusNotifyId);
|
||||
this._keyFocusNotifyId = 0;
|
||||
}
|
||||
|
||||
if (!this._focusWindowChanged > 0) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
metaDisplay.disconnect(this._focusWindowChangedId);
|
||||
this._focusWindowChangedId = 0;
|
||||
}
|
||||
|
||||
let prePopInputMode = global.stage_input_mode;
|
||||
|
||||
if (this._grabbedFromKeynav) {
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
|
||||
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
}
|
||||
|
||||
if (this._prevFocusedWindow) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
if (!metaDisplay.focus_window) {
|
||||
metaDisplay.set_input_focus_window(this._prevFocusedWindow,
|
||||
false, global.get_current_time());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// ignoreRelease:
|
||||
//
|
||||
// Make sure that the next button release event evaluated by the
|
||||
@ -228,12 +274,14 @@ const GrabHelper = new Lang.Class({
|
||||
if (grabStackIndex < 0)
|
||||
return;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||
|
||||
let poppedGrabs = this._grabStack.slice(grabStackIndex);
|
||||
// "Pop" all newly ungrabbed actors off the grab stack
|
||||
// by truncating the array.
|
||||
this._grabStack.length = grabStackIndex;
|
||||
|
||||
let wasModal = this._modalCount > 0;
|
||||
for (let i = poppedGrabs.length - 1; i >= 0; i--) {
|
||||
let poppedGrab = poppedGrabs[i];
|
||||
|
||||
@ -241,63 +289,15 @@ const GrabHelper = new Lang.Class({
|
||||
poppedGrab.onUngrab();
|
||||
|
||||
if (poppedGrab.modal)
|
||||
this._modalCount--;
|
||||
this._releaseModalGrab();
|
||||
|
||||
if (poppedGrab.grabFocus)
|
||||
this._grabFocusCount--;
|
||||
this._releaseFocusGrab();
|
||||
}
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||
|
||||
if (this._grabFocusCount == 0 && this._modalCount == 0)
|
||||
this._fullUngrab(wasModal);
|
||||
|
||||
let poppedGrab = poppedGrabs[0];
|
||||
_navigateActor(poppedGrab.savedFocus, hadFocus);
|
||||
},
|
||||
|
||||
_fullUngrab: function(wasModal) {
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
if (this._eventId > 0) {
|
||||
global.stage.disconnect(this._eventId);
|
||||
this._eventId = 0;
|
||||
}
|
||||
|
||||
if (this._keyFocusNotifyId > 0) {
|
||||
global.stage.disconnect(this._keyFocusNotifyId);
|
||||
this._keyFocusNotifyId = 0;
|
||||
}
|
||||
|
||||
if (!this._focusWindowChanged > 0) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
metaDisplay.disconnect(this._focusWindowChangedId);
|
||||
this._focusWindowChangedId = 0;
|
||||
}
|
||||
|
||||
let prePopInputMode = global.stage_input_mode;
|
||||
|
||||
if (wasModal) {
|
||||
Main.popModal(this._owner);
|
||||
global.sync_pointer();
|
||||
}
|
||||
|
||||
if (this._grabbedFromKeynav) {
|
||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
|
||||
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
}
|
||||
|
||||
if (this._prevFocusedWindow) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
if (!metaDisplay.focus_window) {
|
||||
metaDisplay.set_input_focus_window(this._prevFocusedWindow,
|
||||
false, global.get_current_time());
|
||||
}
|
||||
if (hadFocus) {
|
||||
let poppedGrab = poppedGrabs[0];
|
||||
_navigateActor(poppedGrab.savedFocus);
|
||||
}
|
||||
},
|
||||
|
||||
@ -318,12 +318,15 @@ const GrabHelper = new Lang.Class({
|
||||
if (this._isWithinGrabbedActor(event.get_source()))
|
||||
return false;
|
||||
|
||||
if (Main.keyboard.shouldTakeEvent(event))
|
||||
return false;
|
||||
|
||||
if (button) {
|
||||
// If we have a press event, ignore the next event,
|
||||
// which should be a release event.
|
||||
if (press)
|
||||
this._ignoreRelease = true;
|
||||
this.ungrab();
|
||||
this.ungrab({ actor: this._grabStack[0].actor });
|
||||
}
|
||||
|
||||
return this._modalCount > 0;
|
||||
|
@ -198,7 +198,11 @@ const IconGrid = new Lang.Class({
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
let children = this._getVisibleChildren();
|
||||
let [nColumns, usedWidth] = this._computeLayout(forWidth);
|
||||
let nColumns;
|
||||
if (forWidth < 0)
|
||||
nColumns = children.length;
|
||||
else
|
||||
nColumns = this._computeLayout(forWidth)[0];
|
||||
let nRows;
|
||||
if (nColumns > 0)
|
||||
nRows = Math.ceil(children.length / nColumns);
|
||||
|
@ -200,8 +200,10 @@ const Keyboard = new Lang.Class({
|
||||
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
|
||||
|
||||
this.actor = null;
|
||||
this._focusInTray = false;
|
||||
this._focusInExtendedKeys = false;
|
||||
|
||||
this._timestamp = global.get_current_time();
|
||||
this._timestamp = global.display.get_current_time_roundtrip();
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||
|
||||
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||
@ -288,7 +290,15 @@ const Keyboard = new Lang.Class({
|
||||
|
||||
// Showing an extended key popup and clicking a key from the extended keys
|
||||
// will grab focus, but ignore that
|
||||
if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key)))
|
||||
let extendedKeysWereFocused = this._focusInExtendedKeys;
|
||||
this._focusInExtendedKeys = focus && (focus._extended_keys || focus.extended_key);
|
||||
if (this._focusInExtendedKeys || extendedKeysWereFocused)
|
||||
return;
|
||||
|
||||
// Ignore focus changes caused by message tray showing/hiding
|
||||
let trayWasFocused = this._focusInTray;
|
||||
this._focusInTray = (focus && Main.messageTray.actor.contains(focus));
|
||||
if (this._focusInTray || trayWasFocused)
|
||||
return;
|
||||
|
||||
let time = global.get_current_time();
|
||||
@ -339,6 +349,13 @@ const Keyboard = new Lang.Class({
|
||||
trayButton.reactive = true;
|
||||
trayButton.remove_style_pseudo_class('grayed');
|
||||
}));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, function() {
|
||||
trayButton.reactive = !Main.sessionMode.isLocked;
|
||||
if (Main.sessionMode.isLocked)
|
||||
trayButton.add_style_pseudo_class('grayed');
|
||||
else
|
||||
trayButton.remove_style_pseudo_class('grayed');
|
||||
}));
|
||||
|
||||
return trayButton;
|
||||
},
|
||||
@ -468,6 +485,12 @@ const Keyboard = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
shouldTakeEvent: function(event) {
|
||||
let actor = event.get_source();
|
||||
return Main.layoutManager.keyboardBox.contains(actor) ||
|
||||
actor._extended_keys || actor.extended_key;
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this._redraw();
|
||||
|
||||
@ -494,15 +517,30 @@ const Keyboard = new Lang.Class({
|
||||
this._moveTemporarily();
|
||||
},
|
||||
|
||||
// _compareTimestamp:
|
||||
//
|
||||
// Compare two timestamps taking into account
|
||||
// CURRENT_TIME (0)
|
||||
_compareTimestamp: function(one, two) {
|
||||
if (one == two)
|
||||
return 0;
|
||||
if (one == Clutter.CURRENT_TIME)
|
||||
return 1;
|
||||
if (two == Clutter.CURRENT_TIME)
|
||||
return -1;
|
||||
return one - two;
|
||||
},
|
||||
|
||||
// D-Bus methods
|
||||
Show: function(timestamp) {
|
||||
if (!this._enableKeyboard)
|
||||
return;
|
||||
|
||||
if (timestamp - this._timestamp < 0)
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
this._timestamp = timestamp;
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.show();
|
||||
},
|
||||
|
||||
@ -510,10 +548,11 @@ const Keyboard = new Lang.Class({
|
||||
if (!this._enableKeyboard)
|
||||
return;
|
||||
|
||||
if (timestamp - this._timestamp < 0)
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
this._timestamp = timestamp;
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.hide();
|
||||
},
|
||||
|
||||
@ -543,6 +582,7 @@ const KeyboardSource = new Lang.Class({
|
||||
_init: function(keyboard) {
|
||||
this._keyboard = keyboard;
|
||||
this.parent(_("Keyboard"), 'input-keyboard-symbolic');
|
||||
this.keepTrayOnSummaryClick = true;
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
|
@ -17,6 +17,7 @@ const Tweener = imports.ui.tweener;
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
const STARTUP_ANIMATION_TIME = 0.2;
|
||||
const KEYBOARD_ANIMATION_TIME = 0.5;
|
||||
const PLYMOUTH_TRANSITION_TIME = 1;
|
||||
|
||||
const MonitorConstraint = new Lang.Class({
|
||||
Name: 'MonitorConstraint',
|
||||
@ -42,6 +43,8 @@ const MonitorConstraint = new Lang.Class({
|
||||
},
|
||||
|
||||
set primary(v) {
|
||||
if (v)
|
||||
this._index = -1;
|
||||
this._primary = v;
|
||||
if (this.actor)
|
||||
this.actor.queue_relayout();
|
||||
@ -53,6 +56,7 @@ const MonitorConstraint = new Lang.Class({
|
||||
},
|
||||
|
||||
set index(v) {
|
||||
this._primary = false;
|
||||
this._index = v;
|
||||
if (this.actor)
|
||||
this.actor.queue_relayout();
|
||||
@ -100,6 +104,7 @@ const LayoutManager = new Lang.Class({
|
||||
this.primaryMonitor = null;
|
||||
this.primaryIndex = -1;
|
||||
this._hotCorners = [];
|
||||
this._background = null;
|
||||
this._leftPanelBarrier = 0;
|
||||
this._rightPanelBarrier = 0;
|
||||
this._trayBarrier = 0;
|
||||
@ -120,7 +125,8 @@ const LayoutManager = new Lang.Class({
|
||||
this.panelBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._updatePanelBarriers));
|
||||
|
||||
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
|
||||
this.trayBox = new St.Widget({ name: 'trayBox',
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this.addChrome(this.trayBox);
|
||||
this.trayBox.connect('allocation-changed',
|
||||
Lang.bind(this, this._updateTrayBarrier));
|
||||
@ -318,10 +324,41 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_startupAnimation: function() {
|
||||
this.panelBox.anchor_y = this.panelBox.height;
|
||||
|
||||
let plymouthTransitionRunning = false;
|
||||
|
||||
// If we're the greeter, put up the xrootpmap actor
|
||||
// and fade it out to have a nice transition from plymouth
|
||||
// to the greeter. Otherwise, we'll just animate the panel,
|
||||
// as usual.
|
||||
if (Main.sessionMode.isGreeter) {
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
if (this._background != null) {
|
||||
Main.uiGroup.add_actor(this._background);
|
||||
Tweener.addTween(this._background,
|
||||
{ opacity: 0,
|
||||
time: PLYMOUTH_TRANSITION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: this._fadeBackgroundComplete,
|
||||
onCompleteScope: this });
|
||||
plymouthTransitionRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!plymouthTransitionRunning)
|
||||
this._fadeBackgroundComplete();
|
||||
},
|
||||
|
||||
_fadeBackgroundComplete: function() {
|
||||
// Don't animate the strut
|
||||
this._chrome.freezeUpdateRegions();
|
||||
|
||||
this.panelBox.anchor_y = this.panelBox.height;
|
||||
if (this._background != null) {
|
||||
this._background.destroy();
|
||||
this._background = null;
|
||||
}
|
||||
|
||||
Tweener.addTween(this.panelBox,
|
||||
{ anchor_y: 0,
|
||||
time: STARTUP_ANIMATION_TIME,
|
||||
@ -336,7 +373,6 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
showKeyboard: function () {
|
||||
Main.messageTray.hide();
|
||||
this.keyboardBox.raise_top();
|
||||
Tweener.addTween(this.keyboardBox,
|
||||
{ anchor_y: this.keyboardBox.height,
|
||||
@ -350,6 +386,8 @@ const LayoutManager = new Lang.Class({
|
||||
time: KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this.emit('keyboard-visible-changed', true);
|
||||
},
|
||||
|
||||
_showKeyboardComplete: function() {
|
||||
@ -364,7 +402,6 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
hideKeyboard: function (immediate) {
|
||||
Main.messageTray.hide();
|
||||
if (this._keyboardHeightNotifyId) {
|
||||
this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
|
||||
this._keyboardHeightNotifyId = 0;
|
||||
@ -381,6 +418,8 @@ const LayoutManager = new Lang.Class({
|
||||
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this.emit('keyboard-visible-changed', false);
|
||||
},
|
||||
|
||||
_hideKeyboardComplete: function() {
|
||||
@ -639,7 +678,6 @@ const Chrome = new Lang.Class({
|
||||
|
||||
this._monitors = [];
|
||||
this._inOverview = false;
|
||||
this._isLocked = false;
|
||||
this._updateRegionIdle = 0;
|
||||
this._freezeUpdateCount = 0;
|
||||
|
||||
@ -658,12 +696,9 @@ const Chrome = new Lang.Class({
|
||||
},
|
||||
|
||||
init: function() {
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._overviewHidden));
|
||||
Main.screenShield.connect('lock-status-changed',
|
||||
Lang.bind(this, this._lockStatusChanged));
|
||||
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
},
|
||||
|
||||
addActor: function(actor, params) {
|
||||
@ -749,10 +784,13 @@ const Chrome = new Lang.Class({
|
||||
|
||||
_actorReparented: function(actor, oldParent) {
|
||||
let newParent = actor.get_parent();
|
||||
if (!newParent)
|
||||
if (!newParent) {
|
||||
this._untrackActor(actor);
|
||||
else
|
||||
} else {
|
||||
let i = this._findActor(actor);
|
||||
let actorData = this._trackedActors[i];
|
||||
actorData.isToplevel = (newParent == Main.uiGroup);
|
||||
}
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
@ -763,7 +801,7 @@ const Chrome = new Lang.Class({
|
||||
if (!actorData.isToplevel)
|
||||
continue;
|
||||
|
||||
if (this._inOverview || this._isLocked)
|
||||
if (this._inOverview || !Main.sessionMode.hasWindows)
|
||||
visible = true;
|
||||
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
|
||||
visible = false;
|
||||
@ -785,8 +823,7 @@ const Chrome = new Lang.Class({
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_lockStatusChanged: function(shield, locked) {
|
||||
this._isLocked = locked;
|
||||
_sessionUpdated: function() {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
@ -371,7 +371,8 @@ const ObjInspector = new Lang.Class({
|
||||
|
||||
this._parentList = [];
|
||||
|
||||
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
|
||||
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',
|
||||
@ -443,10 +444,6 @@ const ObjInspector = new Lang.Class({
|
||||
this.actor.show();
|
||||
if (sourceActor) {
|
||||
this.actor.set_scale(0, 0);
|
||||
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
||||
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
|
||||
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
|
||||
Math.floor(sourceY + sourceHeight / 2));
|
||||
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
|
||||
transition: 'easeOutQuad',
|
||||
time: 0.2 });
|
||||
@ -1092,8 +1089,8 @@ const LookingGlass = new Lang.Class({
|
||||
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._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
|
||||
primary.y + this._targetY + Math.floor(myHeight * 0.1));
|
||||
},
|
||||
|
||||
insertObject: function(obj) {
|
||||
|
135
js/ui/main.js
135
js/ui/main.js
@ -10,12 +10,9 @@ const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AutomountManager = imports.ui.automountManager;
|
||||
const AutorunManager = imports.ui.autorunManager;
|
||||
const Components = imports.ui.components;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||
const KeyringPrompt = imports.ui.keyringPrompt;
|
||||
const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
@ -23,11 +20,9 @@ const Keyboard = imports.ui.keyboard;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Overview = imports.ui.overview;
|
||||
const Panel = imports.ui.panel;
|
||||
const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const RunDialog = imports.ui.runDialog;
|
||||
const Layout = imports.ui.layout;
|
||||
const LookingGlass = imports.ui.lookingGlass;
|
||||
const NetworkAgent = imports.ui.networkAgent;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||
const ScreenShield = imports.ui.screenShield;
|
||||
@ -35,7 +30,6 @@ const Scripting = imports.ui.scripting;
|
||||
const SessionMode = imports.ui.sessionMode;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const TelepathyClient = imports.ui.telepathyClient;
|
||||
const UnlockDialog = imports.ui.unlockDialog;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
const Magnifier = imports.ui.magnifier;
|
||||
@ -43,10 +37,9 @@ const XdndHandler = imports.ui.xdndHandler;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2266bbff);
|
||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||
|
||||
let automountManager = null;
|
||||
let autorunManager = null;
|
||||
let componentManager = null;
|
||||
let panel = null;
|
||||
let overview = null;
|
||||
let runDialog = null;
|
||||
@ -56,9 +49,7 @@ let messageTray = null;
|
||||
let screenShield = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let telepathyClient = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let recorder = null;
|
||||
let sessionMode = null;
|
||||
let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
@ -70,7 +61,6 @@ let magnifier = null;
|
||||
let xdndHandler = null;
|
||||
let keyboard = null;
|
||||
let layoutManager = null;
|
||||
let networkAgent = null;
|
||||
let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
@ -78,69 +68,10 @@ let _overridesSettings = null;
|
||||
|
||||
let background = null;
|
||||
|
||||
function createUserSession() {
|
||||
telepathyClient = new TelepathyClient.Client();
|
||||
automountManager = new AutomountManager.AutomountManager();
|
||||
autorunManager = new AutorunManager.AutorunManager();
|
||||
networkAgent = new NetworkAgent.NetworkAgent();
|
||||
|
||||
_initRecorder();
|
||||
}
|
||||
|
||||
function createGDMSession() {
|
||||
screenShield.showDialog();
|
||||
}
|
||||
|
||||
function createGDMLoginDialog(parentActor) {
|
||||
// We do this this here instead of at the top to prevent GDM
|
||||
// related code from getting loaded in normal user sessions
|
||||
const LoginDialog = imports.gdm.loginDialog;
|
||||
|
||||
let loginDialog = new LoginDialog.LoginDialog(parentActor);
|
||||
return [loginDialog, true];
|
||||
}
|
||||
|
||||
function createSessionUnlockDialog(parentActor) {
|
||||
let dialog = new UnlockDialog.UnlockDialog(parentActor);
|
||||
return [dialog, false];
|
||||
}
|
||||
|
||||
function createInitialSetupSession() {
|
||||
networkAgent = new NetworkAgent.NetworkAgent();
|
||||
}
|
||||
|
||||
function _initRecorder() {
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
let desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
||||
let bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
||||
|
||||
global.display.add_keybinding('toggle-recording',
|
||||
bindingSettings,
|
||||
Meta.KeyBindingFlags.NONE, function() {
|
||||
if (recorder == null) {
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
}
|
||||
|
||||
if (recorder.is_recording()) {
|
||||
recorder.close();
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
} else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
// read the parameters from GSettings always in case they have changed
|
||||
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
||||
/* Translators: this is a filename used for screencast recording */
|
||||
// xgettext:no-c-format
|
||||
recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension'));
|
||||
let pipeline = recorderSettings.get_string('pipeline');
|
||||
|
||||
if (!pipeline.match(/^\s*$/))
|
||||
recorder.set_pipeline(pipeline);
|
||||
else
|
||||
recorder.set_pipeline(null);
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
recorder.record();
|
||||
}
|
||||
});
|
||||
function _sessionUpdated() {
|
||||
Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : null);
|
||||
if (sessionMode.isGreeter)
|
||||
screenShield.showDialog();
|
||||
}
|
||||
|
||||
function start() {
|
||||
@ -206,51 +137,34 @@ function start() {
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
overview = new Overview.Overview();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
screenShield = new ScreenShield.ScreenShield();
|
||||
screenSaverDBus = new ShellDBus.ScreenSaverDBus();
|
||||
if (UnlockDialog.isSupported())
|
||||
screenShield = new ScreenShield.ScreenShield();
|
||||
else
|
||||
screenShield = new ScreenShield.ScreenShieldFallback();
|
||||
panel = new Panel.Panel();
|
||||
wm = new WindowManager.WindowManager();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
keyboard = new Keyboard.Keyboard();
|
||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||
componentManager = new Components.ComponentManager();
|
||||
|
||||
sessionMode.createSession();
|
||||
|
||||
panel.init();
|
||||
layoutManager.init();
|
||||
keyboard.init();
|
||||
overview.init();
|
||||
|
||||
if (sessionMode.hasWorkspaces)
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
false, -1, 1);
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
false, -1, 1);
|
||||
Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle));
|
||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||
|
||||
if (sessionMode.hasRunDialog) {
|
||||
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
|
||||
getRunDialog().open();
|
||||
});
|
||||
}
|
||||
|
||||
if (sessionMode.hasOverview) {
|
||||
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
|
||||
overview.toggle();
|
||||
});
|
||||
|
||||
global.display.connect('overlay-key',
|
||||
Lang.bind(overview, overview.toggle));
|
||||
}
|
||||
sessionMode.connect('update', _sessionUpdated);
|
||||
_sessionUpdated();
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
|
||||
// Attempt to become a PolicyKit authentication agent
|
||||
PolkitAuthenticationAgent.init()
|
||||
|
||||
// Become a prompter for gnome keyring
|
||||
KeyringPrompt.init();
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||
@ -275,10 +189,8 @@ function start() {
|
||||
|
||||
_nWorkspacesChanged();
|
||||
|
||||
if (sessionMode.allowExtensions) {
|
||||
ExtensionDownloader.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
}
|
||||
ExtensionDownloader.init();
|
||||
ExtensionSystem.init();
|
||||
}
|
||||
|
||||
let _workspaces = [];
|
||||
@ -522,7 +434,6 @@ function notify(msg, details) {
|
||||
messageTray.add(source);
|
||||
let notification = new MessageTray.Notification(source, msg, details);
|
||||
notification.setTransient(true);
|
||||
notification.setShowWhenLocked(true);
|
||||
source.notify(notification);
|
||||
}
|
||||
|
||||
@ -617,7 +528,7 @@ function _globalKeyPressHandler(actor, event) {
|
||||
if (!sessionMode.hasRunDialog)
|
||||
return false;
|
||||
|
||||
getRunDialog().open();
|
||||
openRunDialog();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||
case Meta.KeyBindingAction.OVERLAY_KEY:
|
||||
@ -763,11 +674,11 @@ function createLookingGlass() {
|
||||
return lookingGlass;
|
||||
}
|
||||
|
||||
function getRunDialog() {
|
||||
function openRunDialog() {
|
||||
if (runDialog == null) {
|
||||
runDialog = new RunDialog.RunDialog();
|
||||
}
|
||||
return runDialog;
|
||||
runDialog.open();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Atk = imports.gi.Atk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -35,8 +36,6 @@ const LONGER_SUMMARY_TIMEOUT = 4;
|
||||
const HIDE_TIMEOUT = 0.2;
|
||||
const LONGER_HIDE_TIMEOUT = 0.6;
|
||||
|
||||
const NOTIFICATION_ICON_SIZE = 24;
|
||||
|
||||
// We delay hiding of the tray if the mouse is within MOUSE_LEFT_ACTOR_THRESHOLD
|
||||
// range from the point where it left the tray.
|
||||
const MOUSE_LEFT_ACTOR_THRESHOLD = 20;
|
||||
@ -143,14 +142,9 @@ const URLHighlighter = new Lang.Class({
|
||||
let url = this._urls[urlId].url;
|
||||
if (url.indexOf(':') == -1)
|
||||
url = 'http://' + url;
|
||||
try {
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
|
||||
return true;
|
||||
} catch (e) {
|
||||
// TODO: remove this after gnome 3 release
|
||||
Util.spawn(['gvfs-open', url]);
|
||||
return true;
|
||||
}
|
||||
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
@ -243,9 +237,7 @@ function makeCloseButton() {
|
||||
closeButton.connect('style-changed', function() {
|
||||
let themeNode = closeButton.get_theme_node();
|
||||
closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
|
||||
|
||||
// libcroco doesn't support negative units
|
||||
closeButton.translation_y = -themeNode.get_length('-shell-close-overlap-y');
|
||||
closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
|
||||
});
|
||||
|
||||
return closeButton;
|
||||
@ -291,9 +283,12 @@ function makeCloseButton() {
|
||||
// If @params contains a 'body' parameter, then that text will be added to
|
||||
// the content area (as with addBody()).
|
||||
//
|
||||
// By default, the icon shown is created by calling
|
||||
// source.createIcon(). However, if @params contains an 'icon'
|
||||
// parameter, the passed in icon will be used.
|
||||
// By default, the icon shown is the same as the source's.
|
||||
// However, if @params contains a 'gicon' parameter, the passed in gicon
|
||||
// will be used.
|
||||
//
|
||||
// You can add a secondary icon to the banner with 'secondaryGIcon'. There
|
||||
// is no fallback for this icon.
|
||||
//
|
||||
// If @params contains a 'titleMarkup', 'bannerMarkup', or
|
||||
// 'bodyMarkup' parameter with the value %true, then the corresponding
|
||||
@ -308,6 +303,8 @@ function makeCloseButton() {
|
||||
const Notification = new Lang.Class({
|
||||
Name: 'Notification',
|
||||
|
||||
ICON_SIZE: 24,
|
||||
|
||||
IMAGE_SIZE: 125,
|
||||
|
||||
_init: function(source, title, banner, params) {
|
||||
@ -318,7 +315,7 @@ const Notification = new Lang.Class({
|
||||
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
|
||||
this.isTransient = false;
|
||||
this.expanded = false;
|
||||
this.showWhenLocked = false;
|
||||
this.focused = false;
|
||||
this.acknowledged = false;
|
||||
this._destroyed = false;
|
||||
this._useActionIcons = false;
|
||||
@ -391,8 +388,8 @@ const Notification = new Lang.Class({
|
||||
update: function(title, banner, params) {
|
||||
params = Params.parse(params, { customContent: false,
|
||||
body: null,
|
||||
icon: null,
|
||||
secondaryIcon: null,
|
||||
gicon: null,
|
||||
secondaryGIcon: null,
|
||||
titleMarkup: false,
|
||||
bannerMarkup: false,
|
||||
bodyMarkup: false,
|
||||
@ -402,12 +399,12 @@ const Notification = new Lang.Class({
|
||||
|
||||
let oldFocus = global.stage.key_focus;
|
||||
|
||||
if (this._icon && (params.icon || params.clear)) {
|
||||
if (this._icon && (params.gicon || params.clear)) {
|
||||
this._icon.destroy();
|
||||
this._icon = null;
|
||||
}
|
||||
|
||||
if (this._secondaryIcon && (params.secondaryIcon || params.clear)) {
|
||||
if (this._secondaryIcon && (params.secondaryGIcon || params.clear)) {
|
||||
this._secondaryIcon.destroy();
|
||||
this._secondaryIcon = null;
|
||||
}
|
||||
@ -437,8 +434,14 @@ const Notification = new Lang.Class({
|
||||
if (!this._scrollArea && !this._actionArea && !this._imageBin)
|
||||
this._table.remove_style_class_name('multi-line-notification');
|
||||
|
||||
if (!this._icon) {
|
||||
this._icon = params.icon || this.source.createIcon(NOTIFICATION_ICON_SIZE);
|
||||
if (params.gicon) {
|
||||
this._icon = new St.Icon({ gicon: params.gicon,
|
||||
icon_size: this.ICON_SIZE });
|
||||
} else {
|
||||
this._icon = this.source.createIcon(this.ICON_SIZE);
|
||||
}
|
||||
|
||||
if (this._icon) {
|
||||
this._table.add(this._icon, { row: 0,
|
||||
col: 0,
|
||||
x_expand: false,
|
||||
@ -447,11 +450,10 @@ const Notification = new Lang.Class({
|
||||
y_align: St.Align.START });
|
||||
}
|
||||
|
||||
if (!this._secondaryIcon) {
|
||||
this._secondaryIcon = params.secondaryIcon;
|
||||
|
||||
if (this._secondaryIcon)
|
||||
this._bannerBox.add_actor(this._secondaryIcon);
|
||||
if (params.secondaryGIcon) {
|
||||
this._secondaryIcon = new St.Icon({ gicon: params.secondaryGIcon,
|
||||
style_class: 'secondary-icon' });
|
||||
this._bannerBox.add_actor(this._secondaryIcon);
|
||||
}
|
||||
|
||||
this.title = title;
|
||||
@ -711,14 +713,6 @@ const Notification = new Lang.Class({
|
||||
this.isTransient = isTransient;
|
||||
},
|
||||
|
||||
setShowWhenLocked: function(show) {
|
||||
if (show && !this.isTransient) {
|
||||
throw new Error('ShowWhenLocked can only be set on a transient notification');
|
||||
}
|
||||
|
||||
this.showWhenLocked = show;
|
||||
},
|
||||
|
||||
setUseActionIcons: function(useIcons) {
|
||||
this._useActionIcons = useIcons;
|
||||
},
|
||||
@ -952,11 +946,22 @@ const SourceActor = new Lang.Class({
|
||||
}));
|
||||
this._actorDestroyed = false;
|
||||
|
||||
this._counterLabel = new St.Label();
|
||||
this._counterLabel = new St.Label( {x_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
y_expand: true });
|
||||
|
||||
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
|
||||
child: this._counterLabel });
|
||||
child: this._counterLabel,
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this._counterBin.hide();
|
||||
|
||||
this._counterBin.connect('style-changed', Lang.bind(this, function() {
|
||||
let themeNode = this._counterBin.get_theme_node();
|
||||
this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
|
||||
this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
|
||||
}));
|
||||
|
||||
this._iconBin = new St.Bin({ width: size,
|
||||
height: size,
|
||||
x_fill: true,
|
||||
@ -1025,7 +1030,14 @@ const SourceActor = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._counterBin.visible = this._source.countVisible;
|
||||
this._counterLabel.set_text(this._source.count.toString());
|
||||
|
||||
let text;
|
||||
if (this._source.count < 100)
|
||||
text = this._source.count.toString();
|
||||
else
|
||||
text = String.fromCharCode(0x22EF); // midline horizontal ellipsis
|
||||
|
||||
this._counterLabel.set_text(text);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1042,6 +1054,7 @@ const Source = new Lang.Class({
|
||||
this.isChat = false;
|
||||
this.isMuted = false;
|
||||
this.showInLockScreen = true;
|
||||
this.keepTrayOnSummaryClick = false;
|
||||
|
||||
this.notifications = [];
|
||||
},
|
||||
@ -1062,6 +1075,27 @@ const Source = new Lang.Class({
|
||||
this.emit('count-updated');
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
let item;
|
||||
let rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
|
||||
vertical: true });
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open"));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this.open();
|
||||
this.emit('done-displaying-content');
|
||||
}));
|
||||
rightClickMenu.add(item.actor);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Remove"));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
this.emit('done-displaying-content');
|
||||
}));
|
||||
rightClickMenu.add(item.actor);
|
||||
return rightClickMenu;
|
||||
},
|
||||
|
||||
setTransient: function(isTransient) {
|
||||
this.isTransient = isTransient;
|
||||
},
|
||||
@ -1082,10 +1116,14 @@ const Source = new Lang.Class({
|
||||
// Provides a sane default implementation, override if you need
|
||||
// something more fancy.
|
||||
createIcon: function(size) {
|
||||
return new St.Icon({ icon_name: this.iconName,
|
||||
return new St.Icon({ gicon: this.getIcon(),
|
||||
icon_size: size });
|
||||
},
|
||||
|
||||
getIcon: function() {
|
||||
return new Gio.ThemedIcon({ name: this.iconName });
|
||||
},
|
||||
|
||||
_ensureMainIcon: function() {
|
||||
if (this._mainIcon)
|
||||
return;
|
||||
@ -1190,6 +1228,7 @@ const SummaryItem = new Lang.Class({
|
||||
can_focus: true,
|
||||
track_hover: true });
|
||||
this.actor.label_actor = new St.Label({ text: source.title });
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
||||
this._sourceBox = new St.BoxLayout({ style_class: 'summary-source' });
|
||||
|
||||
this._sourceIcon = source.getSummaryIcon();
|
||||
@ -1198,13 +1237,6 @@ const SummaryItem = new Lang.Class({
|
||||
|
||||
this.notificationStackWidget = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
|
||||
this._closeButton = makeCloseButton();
|
||||
this._closeButton.connect('clicked', Lang.bind(this, function() {
|
||||
source.destroy();
|
||||
this.emit('done-displaying-content');
|
||||
}));
|
||||
|
||||
this.notificationStackWidget.add_actor(this._closeButton);
|
||||
this.notificationStackView = new St.ScrollView({ style_class: source.isChat ? '' : 'summary-notification-stack-scrollview',
|
||||
vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
@ -1213,6 +1245,9 @@ const SummaryItem = new Lang.Class({
|
||||
vertical: true });
|
||||
this.notificationStackView.add_actor(this.notificationStack);
|
||||
this.notificationStackWidget.add_actor(this.notificationStackView);
|
||||
|
||||
this.closeButton = makeCloseButton();
|
||||
this.notificationStackWidget.add_actor(this.closeButton);
|
||||
this._stackedNotifications = [];
|
||||
|
||||
this._oldMaxScrollAdjustment = 0;
|
||||
@ -1224,46 +1259,17 @@ const SummaryItem = new Lang.Class({
|
||||
this._oldMaxScrollAdjustment = adjustment.upper;
|
||||
}));
|
||||
|
||||
this.rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
|
||||
vertical: true });
|
||||
|
||||
let item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open"));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
source.open();
|
||||
this.emit('done-displaying-content');
|
||||
}));
|
||||
this.rightClickMenu.add(item.actor);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Remove"));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
source.destroy();
|
||||
this.emit('done-displaying-content');
|
||||
}));
|
||||
this.rightClickMenu.add(item.actor);
|
||||
|
||||
if (source.isChat) {
|
||||
item = new PopupMenu.PopupMenuItem('');
|
||||
item.actor.connect('notify::mapped', Lang.bind(this, function() {
|
||||
item.label.set_text(source.isMuted ? _("Unmute") : _("Mute"));
|
||||
}));
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
source.setMuted(!source.isMuted);
|
||||
this.emit('done-displaying-content');
|
||||
}));
|
||||
this.rightClickMenu.add(item.actor);
|
||||
}
|
||||
|
||||
global.focus_manager.add_group(this.rightClickMenu);
|
||||
this.rightClickMenu = source.buildRightClickMenu();
|
||||
if (this.rightClickMenu)
|
||||
global.focus_manager.add_group(this.rightClickMenu);
|
||||
},
|
||||
|
||||
get closeButtonVisible() {
|
||||
return this._closeButton.visible;
|
||||
},
|
||||
|
||||
set closeButtonVisible(v) {
|
||||
this._closeButton.visible = v;
|
||||
_onKeyPress: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Up) {
|
||||
actor.emit('clicked', 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
prepareNotificationStackForShowing: function() {
|
||||
@ -1330,7 +1336,7 @@ const SummaryItem = new Lang.Class({
|
||||
},
|
||||
|
||||
_notificationDoneDisplaying: function() {
|
||||
this.emit('done-displaying-content');
|
||||
this.source.emit('done-displaying-content');
|
||||
},
|
||||
|
||||
_notificationDestroyed: function(notification) {
|
||||
@ -1368,39 +1374,47 @@ const MessageTray = new Lang.Class({
|
||||
}));
|
||||
|
||||
this.actor = new St.Widget({ name: 'message-tray',
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||
track_hover: true,
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.START,
|
||||
});
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._onTrayHoverChanged));
|
||||
|
||||
this._notificationWidget = new St.Widget({ name: 'notification-container',
|
||||
y_align: Clutter.ActorAlign.START,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_expand: true,
|
||||
x_expand: true,
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this.actor.add_actor(this._notificationWidget);
|
||||
|
||||
this._notificationBin = new St.Bin();
|
||||
this._notificationBin = new St.Bin({ y_expand: true });
|
||||
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
|
||||
this._notificationWidget.add_actor(this._notificationBin);
|
||||
this._notificationWidget.hide();
|
||||
this._notificationQueue = [];
|
||||
this._notification = null;
|
||||
this._notificationClickedId = 0;
|
||||
|
||||
this._summaryBin = new St.Bin({ x_align: St.Align.END,
|
||||
y_expand: true, // this is the Clutter property
|
||||
reactive: true });
|
||||
this._summaryBin.connect('button-release-event', Lang.bind(this, function(actor, event) {
|
||||
this.actor.connect('button-release-event', Lang.bind(this, function(actor, event) {
|
||||
this._setClickedSummaryItem(null);
|
||||
this._updateState();
|
||||
actor.grab_key_focus();
|
||||
}));
|
||||
this.actor.add_actor(this._summaryBin);
|
||||
global.focus_manager.add_group(this.actor);
|
||||
this._summary = new St.BoxLayout({ name: 'summary-mode',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
global.focus_manager.add_group(this._summary);
|
||||
track_hover: true,
|
||||
x_align: Clutter.ActorAlign.END,
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
y_expand: true });
|
||||
this._summary.connect('notify::hover', Lang.bind(this, this._onSummaryHoverChanged));
|
||||
this._summaryBin.child = this._summary;
|
||||
this._summaryBin.opacity = 0;
|
||||
this.actor.add_actor(this._summary);
|
||||
this._summary.opacity = 0;
|
||||
|
||||
this._summaryMotionId = 0;
|
||||
this._trayMotionId = 0;
|
||||
@ -1408,6 +1422,8 @@ const MessageTray = new Lang.Class({
|
||||
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
{ reactive: true,
|
||||
track_hover: true });
|
||||
this._summaryBoxPointer.actor.connect('key-press-event',
|
||||
Lang.bind(this, this._onSummaryBoxPointerKeyPress));
|
||||
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
Main.layoutManager.addChrome(this._summaryBoxPointer.actor);
|
||||
@ -1425,16 +1441,19 @@ const MessageTray = new Lang.Class({
|
||||
this._closeButton.connect('clicked', Lang.bind(this, this._onCloseClicked));
|
||||
this._notificationWidget.add_actor(this._closeButton);
|
||||
|
||||
this._idleMonitorWatchId = 0;
|
||||
this._idleMonitorBecameActiveId = 0;
|
||||
this._userActiveWhileNotificationShown = false;
|
||||
|
||||
this.idleMonitor = Shell.IdleMonitor.get();
|
||||
this.idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
|
||||
this._grabHelper = new GrabHelper.GrabHelper(this.actor);
|
||||
this._grabHelper.addActor(this._summaryBoxPointer.actor);
|
||||
this._grabHelper.addActor(this.actor);
|
||||
if (Main.panel.statusArea.activities)
|
||||
this._grabHelper.addActor(Main.panel.statusArea.activities.hotCorner.actor);
|
||||
|
||||
Main.layoutManager.keyboardBox.connect('notify::hover', Lang.bind(this, this._onKeyboardHoverChanged));
|
||||
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged));
|
||||
|
||||
this._trayState = State.HIDDEN;
|
||||
this._locked = false;
|
||||
@ -1443,13 +1462,16 @@ const MessageTray = new Lang.Class({
|
||||
this._trayLeftTimeoutId = 0;
|
||||
this._pointerInTray = false;
|
||||
this._pointerInKeyboard = false;
|
||||
this._keyboardVisible = false;
|
||||
this._summaryState = State.HIDDEN;
|
||||
this._pointerInSummary = false;
|
||||
this._notificationClosed = false;
|
||||
this._notificationState = State.HIDDEN;
|
||||
this._notificationTimeoutId = 0;
|
||||
this._notificationExpandedId = 0;
|
||||
this._summaryBoxPointerState = State.HIDDEN;
|
||||
this._summaryBoxPointerTimeoutId = 0;
|
||||
this._desktopCloneState = State.HIDDEN;
|
||||
this._overviewVisible = Main.overview.visible;
|
||||
this._notificationRemoved = false;
|
||||
this._reNotifyAfterHideNotification = null;
|
||||
@ -1469,16 +1491,14 @@ const MessageTray = new Lang.Class({
|
||||
Main.layoutManager.trackChrome(this._notificationWidget);
|
||||
Main.layoutManager.trackChrome(this._closeButton);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, function() {
|
||||
this.actor.style_changed();
|
||||
}));
|
||||
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this,
|
||||
function() {
|
||||
this._overviewVisible = true;
|
||||
this._grabHelper.ungrab(); // drop modal grab if necessary
|
||||
this.actor.add_style_pseudo_class('overview');
|
||||
this._updateState();
|
||||
this.show();
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this,
|
||||
function() {
|
||||
@ -1488,8 +1508,7 @@ const MessageTray = new Lang.Class({
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._isScreenLocked = false;
|
||||
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onScreenLockStatusChanged));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
|
||||
global.display.add_keybinding('toggle-message-tray',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
@ -1503,6 +1522,19 @@ const MessageTray = new Lang.Class({
|
||||
pointerWatcher.addWatch(TRAY_DWELL_CHECK_INTERVAL, Lang.bind(this, this._checkTrayDwell));
|
||||
this._trayDwellTimeoutId = 0;
|
||||
this._trayDwelling = false;
|
||||
this._trayDwellUserTime = 0;
|
||||
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
if (Main.sessionMode.isLocked || Main.sessionMode.isGreeter)
|
||||
Main.ctrlAltTabManager.removeGroup(this._summary);
|
||||
else
|
||||
Main.ctrlAltTabManager.addGroup(this._summary, _("Message Tray"), 'start-here-symbolic',
|
||||
{ focusCallback: Lang.bind(this, this.toggleAndNavigate),
|
||||
sortGroup: CtrlAltTab.SortGroup.BOTTOM });
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_checkTrayDwell: function(x, y) {
|
||||
@ -1516,9 +1548,14 @@ const MessageTray = new Lang.Class({
|
||||
// of the monitor. The _trayDwelling variable is used so that we only try to
|
||||
// fire off one tray dwell - if it fails (because, say, the user has the mouse down),
|
||||
// we don't try again until the user moves the mouse up and down again.
|
||||
if (!this._trayDwelling && !this.actor.hover && this._trayDwellTimeoutId == 0)
|
||||
if (!this._trayDwelling && !this.actor.hover && this._trayDwellTimeoutId == 0) {
|
||||
// Save the interaction timestamp so we can detect user input
|
||||
let focusWindow = global.display.focus_window;
|
||||
this._trayDwellUserTime = focusWindow ? focusWindow.user_time : 0;
|
||||
|
||||
this._trayDwellTimeoutId = Mainloop.timeout_add(TRAY_DWELL_TIME,
|
||||
Lang.bind(this, this._trayDwellTimeout));
|
||||
}
|
||||
this._trayDwelling = true;
|
||||
} else {
|
||||
this._cancelTrayDwell();
|
||||
@ -1534,6 +1571,16 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_trayDwellTimeout: function() {
|
||||
if (Main.modalCount > 0)
|
||||
return false;
|
||||
|
||||
// If the user interacted with the focus window since we started the tray
|
||||
// dwell (by clicking or typing), don't activate the message tray
|
||||
let focusWindow = global.display.focus_window;
|
||||
let currentUserTime = focusWindow ? focusWindow.user_time : 0;
|
||||
if (currentUserTime != this._trayDwellUserTime)
|
||||
return false;
|
||||
|
||||
this._trayDwellTimeoutId = 0;
|
||||
|
||||
this._traySummoned = true;
|
||||
@ -1544,21 +1591,13 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
_onCloseClicked: function() {
|
||||
if (this._notificationState == State.SHOWN) {
|
||||
this._notification.emit('done-displaying');
|
||||
if (!this._notification.resident)
|
||||
this._notification.destroy();
|
||||
this._closeButton.hide();
|
||||
this._notificationClosed = true;
|
||||
this._updateState();
|
||||
this._notificationClosed = false;
|
||||
}
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
let monitor = Main.layoutManager.bottomMonitor;
|
||||
|
||||
let width = this._notificationWidget.get_width();
|
||||
this._notificationWidget.x = Math.floor((monitor.width - width) / 2);
|
||||
this._summaryBin.x = 0;
|
||||
this._summaryBin.width = monitor.width;
|
||||
},
|
||||
|
||||
contains: function(source) {
|
||||
return this._getIndexOfSummaryItemForSource(source) >= 0;
|
||||
},
|
||||
@ -1668,11 +1707,6 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationQueue.splice(index, 1);
|
||||
},
|
||||
|
||||
_onScreenLockStatusChanged: function(screenShield, locked) {
|
||||
this._isScreenLocked = locked;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_lock: function() {
|
||||
this._locked = true;
|
||||
},
|
||||
@ -1691,10 +1725,8 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
toggleAndNavigate: function() {
|
||||
// Grab the key focus so that GrabHelper knows
|
||||
// that we have the key grab.
|
||||
this._summary.grab_key_focus();
|
||||
this.toggle();
|
||||
this._summary.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
@ -1704,6 +1736,11 @@ const MessageTray = new Lang.Class({
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._traySummoned = true;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_onNotify: function(source, notification) {
|
||||
if (this._summaryBoxPointerItem && this._summaryBoxPointerItem.source == source) {
|
||||
if (this._summaryBoxPointerState == State.HIDING) {
|
||||
@ -1747,7 +1784,10 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
_onSummaryItemClicked: function(summaryItem, button) {
|
||||
if (summaryItem.source.handleSummaryClick()) {
|
||||
this._escapeTray();
|
||||
if (summaryItem.source.keepTrayOnSummaryClick)
|
||||
this._setClickedSummaryItem(null);
|
||||
else
|
||||
this._escapeTray();
|
||||
} else {
|
||||
if (!this._setClickedSummaryItem(summaryItem, button))
|
||||
this._setClickedSummaryItem(null);
|
||||
@ -1838,6 +1878,20 @@ const MessageTray = new Lang.Class({
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_onKeyboardVisibleChanged: function(layoutManager, keyboardVisible) {
|
||||
if (this._keyboardVisible == keyboardVisible)
|
||||
return;
|
||||
|
||||
this._keyboardVisible = keyboardVisible;
|
||||
|
||||
if (keyboardVisible)
|
||||
this.actor.add_style_pseudo_class('keyboard');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('keyboard');
|
||||
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_onFullscreenChanged: function(obj, state) {
|
||||
this._inFullscreen = state;
|
||||
this._updateState();
|
||||
@ -1898,34 +1952,26 @@ const MessageTray = new Lang.Class({
|
||||
// at the present time.
|
||||
_updateState: function() {
|
||||
// Notifications
|
||||
let notificationQueue = this._notificationQueue.filter(Lang.bind(this, function(notification) {
|
||||
if (this._isScreenLocked)
|
||||
return notification.showWhenLocked;
|
||||
else
|
||||
return true;
|
||||
}));
|
||||
let notificationQueue = this._notificationQueue;
|
||||
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
|
||||
// notificationsLimited is false when the screen is locked, because they go through
|
||||
// different filtering, and we want to show non urgent messages at times
|
||||
let notificationsLimited = (this._busy || this._inFullscreen) && !this._isScreenLocked;
|
||||
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent);
|
||||
let notificationsLimited = this._busy || this._inFullscreen;
|
||||
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent) && Main.sessionMode.hasNotifications;
|
||||
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
|
||||
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
|
||||
let notificationExpanded = this._notification && this._notification.expanded;
|
||||
let notificationExpired = this._notificationTimeoutId == 0 &&
|
||||
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
|
||||
!(this._notification && this._notification.focused) &&
|
||||
!this._pointerInTray &&
|
||||
!this._locked &&
|
||||
!(this._pointerInKeyboard && notificationExpanded);
|
||||
let notificationLockedOut = this._isScreenLocked && (this._notification && !this._notification.showWhenLocked);
|
||||
// TODO: how to deal with locked out notiifcations if want to keep showing notifications?!
|
||||
let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown);
|
||||
let notificationLockedOut = !Main.sessionMode.hasNotifications && this._notification;
|
||||
let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown) || this._notificationClosed;
|
||||
let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
|
||||
|
||||
if (this._notificationState == State.HIDDEN) {
|
||||
if (canShowNotification) {
|
||||
this._showNotification(nextNotification);
|
||||
this._notificationQueue.splice(this._notificationQueue.indexOf(nextNotification), 1);
|
||||
this._showNotification();
|
||||
}
|
||||
} else if (this._notificationState == State.SHOWN) {
|
||||
if (notificationMustClose)
|
||||
@ -1937,7 +1983,7 @@ const MessageTray = new Lang.Class({
|
||||
}
|
||||
|
||||
// Summary
|
||||
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
|
||||
let summarySummoned = this._pointerInSummary || this._traySummoned;
|
||||
let summaryPinned = this._pointerInTray || summarySummoned || this._locked;
|
||||
let summaryHovered = this._pointerInTray || this._pointerInSummary;
|
||||
|
||||
@ -1945,9 +1991,9 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationState == State.SHOWN);
|
||||
let notificationsDone = !notificationsVisible && !notificationsPending;
|
||||
|
||||
let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered;
|
||||
let summaryOptionalInOverview = !this._locked && !summaryHovered;
|
||||
let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
|
||||
|| notificationsVisible || this._isScreenLocked;
|
||||
|| notificationsVisible || !Main.sessionMode.hasNotifications;
|
||||
|
||||
if (this._summaryState == State.HIDDEN && !mustHideSummary && summarySummoned)
|
||||
this._showSummary();
|
||||
@ -1986,9 +2032,22 @@ const MessageTray = new Lang.Class({
|
||||
let trayShouldBeVisible = (this._summaryState == State.SHOWING ||
|
||||
this._summaryState == State.SHOWN);
|
||||
if (!trayIsVisible && trayShouldBeVisible)
|
||||
this._showTray();
|
||||
trayShouldBeVisible = this._showTray();
|
||||
else if (trayIsVisible && !trayShouldBeVisible)
|
||||
this._hideTray();
|
||||
|
||||
// Desktop clone
|
||||
let desktopCloneIsVisible = (this._desktopCloneState == State.SHOWING ||
|
||||
this._desktopCloneState == State.SHOWN);
|
||||
let desktopCloneShouldBeVisible = (trayShouldBeVisible &&
|
||||
!this._overviewVisible &&
|
||||
!this._keyboardVisible);
|
||||
|
||||
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) {
|
||||
this._showDesktopClone();
|
||||
} else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) {
|
||||
this._hideDesktopClone (this._keyboardVisible);
|
||||
}
|
||||
},
|
||||
|
||||
_tween: function(actor, statevar, value, params) {
|
||||
@ -2017,11 +2076,13 @@ const MessageTray = new Lang.Class({
|
||||
// Don't actually take a modal grab in the overview.
|
||||
// Just add something to the grab stack that we can
|
||||
// pop later.
|
||||
let modal = !this._overviewVisible;
|
||||
|
||||
if (!this._grabHelper.grab({ actor: this.actor,
|
||||
modal: !this._overviewVisible,
|
||||
modal: modal,
|
||||
onUngrab: Lang.bind(this, this._escapeTray) })) {
|
||||
this._traySummoned = false;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this._tween(this.actor, '_trayState', State.SHOWN,
|
||||
@ -2030,49 +2091,48 @@ const MessageTray = new Lang.Class({
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
// Don't move the windows up if we are in the overview,
|
||||
// but show the tray in the ctrl+alt+tab list.
|
||||
if (this._overviewVisible) {
|
||||
Main.ctrlAltTabManager.addGroup(this._summary, _("Message Tray"), 'start-here-symbolic',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.BOTTOM });
|
||||
return;
|
||||
}
|
||||
if (!this._overviewVisible)
|
||||
this._lightbox.show();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_updateDesktopCloneClip: function() {
|
||||
let geometry = this._bottomMonitorGeometry;
|
||||
let progress = -Math.round(this._desktopClone.y);
|
||||
this._desktopClone.set_clip(geometry.x,
|
||||
geometry.y + progress,
|
||||
geometry.width,
|
||||
geometry.height - progress);
|
||||
},
|
||||
|
||||
_showDesktopClone: function() {
|
||||
let bottomMonitor = Main.layoutManager.bottomMonitor;
|
||||
let geometry = new Clutter.Geometry({ x: bottomMonitor.x,
|
||||
y: bottomMonitor.y,
|
||||
width: bottomMonitor.width,
|
||||
height: bottomMonitor.height
|
||||
});
|
||||
this._desktopClone = new Clutter.Clone({ source: global.window_group, clip: geometry });
|
||||
this._bottomMonitorGeometry = { x: bottomMonitor.x,
|
||||
y: bottomMonitor.y,
|
||||
width: bottomMonitor.width,
|
||||
height: bottomMonitor.height };
|
||||
|
||||
if (this._desktopClone)
|
||||
this._desktopClone.destroy();
|
||||
this._desktopClone = new Clutter.Clone({ source: global.window_group, clip: new Clutter.Geometry(this._bottomMonitorGeometry) });
|
||||
Main.uiGroup.insert_child_above(this._desktopClone, global.window_group);
|
||||
this._desktopClone.x = 0;
|
||||
this._desktopClone.y = 0;
|
||||
this._desktopClone.show();
|
||||
|
||||
this._lightbox.show();
|
||||
|
||||
this._desktopClone._progress = 0;
|
||||
Tweener.addTween(this._desktopClone,
|
||||
{ _progress: this.actor.height,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onUpdate: Lang.bind(this, function() {
|
||||
let progress = Math.round(this._desktopClone._progress);
|
||||
this._desktopClone.y = - progress;
|
||||
this._desktopClone.set_clip(geometry.x,
|
||||
geometry.y + progress,
|
||||
geometry.width,
|
||||
geometry.height - progress);
|
||||
})
|
||||
});
|
||||
this._tween(this._desktopClone, '_desktopCloneState', State.SHOWN,
|
||||
{ y: -this.actor.height,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onUpdate: Lang.bind(this, this._updateDesktopCloneClip)
|
||||
});
|
||||
},
|
||||
|
||||
_hideTray: function() {
|
||||
// Note that we might have entered here without a grab,
|
||||
// which would happen if GrabHelper ungrabbed for us.
|
||||
// This is a no-op in that case.
|
||||
this._grabHelper.ungrab({ actor: this.actor });
|
||||
// Having the summary item animate out while sliding down the tray
|
||||
// is distracting, so hide it immediately in case it was visible.
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
|
||||
this._tween(this.actor, '_trayState', State.HIDDEN,
|
||||
{ y: 0,
|
||||
@ -2080,52 +2140,52 @@ const MessageTray = new Lang.Class({
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
// If we are coming back from the overview, there are no windows
|
||||
// to be moved. Just remove the tray from the ctrl+alt+tab list.
|
||||
if (!this._desktopClone) {
|
||||
Main.ctrlAltTabManager.removeGroup(this._summary);
|
||||
return;
|
||||
}
|
||||
|
||||
let geometry = this._desktopClone.clip;
|
||||
this._desktopClone._progress = 0;
|
||||
Tweener.addTween(this._desktopClone,
|
||||
{ _progress: this.actor.height,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._desktopClone.destroy();
|
||||
this._desktopClone = null;
|
||||
}),
|
||||
onUpdate: Lang.bind(this, function() {
|
||||
let progress = Math.round(this._desktopClone._progress);
|
||||
this._desktopClone.y = progress - this.actor.height;
|
||||
this._desktopClone.set_clip(geometry.x,
|
||||
geometry.y - progress,
|
||||
geometry.width,
|
||||
geometry.height + progress);
|
||||
})
|
||||
});
|
||||
|
||||
// Note that we might have entered here without a grab,
|
||||
// which would happen if GrabHelper ungrabbed for us.
|
||||
// This is a no-op in that case.
|
||||
this._grabHelper.ungrab({ actor: this.actor });
|
||||
this._lightbox.hide();
|
||||
},
|
||||
|
||||
_onIdleMonitorWatch: function(monitor, id, userBecameIdle) {
|
||||
this.idleMonitor.remove_watch(this._idleMonitorWatchId);
|
||||
this._idleMonitorWatchId = 0;
|
||||
if (!userBecameIdle)
|
||||
this._updateNotificationTimeout(2000);
|
||||
_hideDesktopClone: function(now) {
|
||||
this._tween(this._desktopClone, '_desktopCloneState', State.HIDDEN,
|
||||
{ y: 0,
|
||||
time: now ? 0 : ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._desktopClone.destroy();
|
||||
this._desktopClone = null;
|
||||
this._bottomMonitorGeometry = null;
|
||||
}),
|
||||
onUpdate: Lang.bind(this, this._updateDesktopCloneClip)
|
||||
});
|
||||
},
|
||||
|
||||
_onIdleMonitorBecameActive: function() {
|
||||
this.idleMonitor.disconnect(this._idleMonitorBecameActiveId);
|
||||
this._idleMonitorBecameActiveId = 0;
|
||||
|
||||
this._userActiveWhileNotificationShown = true;
|
||||
this._updateNotificationTimeout(2000);
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_showNotification: function(notification) {
|
||||
this._notification = notification;
|
||||
this._userActiveWhileNotificationShown = this.idleMonitor.get_idletime() <= IDLE_TIME;
|
||||
this._idleMonitorWatchId = this.idleMonitor.add_watch(IDLE_TIME,
|
||||
Lang.bind(this, this._onIdleMonitorWatch));
|
||||
_showNotification: function() {
|
||||
this._notification = this._notificationQueue.shift();
|
||||
|
||||
let userIdle = this.idleMonitor.get_idletime() > IDLE_TIME;
|
||||
if (userIdle) {
|
||||
this._userActiveWhileNotificationShown = false;
|
||||
this._idleMonitorBecameActiveId = this.idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||
} else {
|
||||
this._userActiveWhileNotificationShown = true;
|
||||
}
|
||||
|
||||
this._notificationClickedId = this._notification.connect('done-displaying',
|
||||
Lang.bind(this, this._escapeTray));
|
||||
this._notification.connect('unfocused', Lang.bind(this, function() {
|
||||
this._updateState();
|
||||
}));
|
||||
this._notificationBin.child = this._notification.actor;
|
||||
|
||||
this._notificationWidget.opacity = 0;
|
||||
@ -2233,6 +2293,11 @@ const MessageTray = new Lang.Class({
|
||||
_hideNotification: function() {
|
||||
this._grabHelper.ungrab({ actor: this._notification.actor });
|
||||
|
||||
if (this._idleMonitorBecameActiveId) {
|
||||
this.idleMonitor.disconnect(this._idleMonitorBecameActiveId);
|
||||
this._idleMonitorBecameActiveId = 0;
|
||||
}
|
||||
|
||||
if (this._notificationExpandedId) {
|
||||
this._notification.disconnect(this._notificationExpandedId);
|
||||
this._notificationExpandedId = 0;
|
||||
@ -2311,18 +2376,16 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_showSummary: function() {
|
||||
this._summaryBin.opacity = 0;
|
||||
this._summaryBin.y = this.actor.height;
|
||||
this._tween(this._summaryBin, '_summaryState', State.SHOWN,
|
||||
{ y: 0,
|
||||
opacity: 255,
|
||||
this._summary.opacity = 0;
|
||||
this._tween(this._summary, '_summaryState', State.SHOWN,
|
||||
{ opacity: 255,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
});
|
||||
},
|
||||
|
||||
_hideSummary: function() {
|
||||
this._tween(this._summaryBin, '_summaryState', State.HIDDEN,
|
||||
this._tween(this._summary, '_summaryState', State.HIDDEN,
|
||||
{ opacity: 0,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
@ -2333,9 +2396,11 @@ const MessageTray = new Lang.Class({
|
||||
this._summaryBoxPointerItem = this._clickedSummaryItem;
|
||||
this._summaryBoxPointerContentUpdatedId = this._summaryBoxPointerItem.connect('content-updated',
|
||||
Lang.bind(this, this._onSummaryBoxPointerContentUpdated));
|
||||
this._summaryBoxPointerDoneDisplayingId = this._summaryBoxPointerItem.connect('done-displaying-content',
|
||||
Lang.bind(this, this._escapeTray));
|
||||
if (this._clickedSummaryItemMouseButton == 1) {
|
||||
this._sourceDoneDisplayingId = this._summaryBoxPointerItem.source.connect('done-displaying-content',
|
||||
Lang.bind(this, this._escapeTray));
|
||||
|
||||
let hasRightClickMenu = this._summaryBoxPointerItem.rightClickMenu != null;
|
||||
if (this._clickedSummaryItemMouseButton == 1 || !hasRightClickMenu) {
|
||||
let newQueue = [];
|
||||
for (let i = 0; i < this._notificationQueue.length; i++) {
|
||||
let notification = this._notificationQueue[i];
|
||||
@ -2348,14 +2413,16 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationQueue = newQueue;
|
||||
|
||||
this._summaryBoxPointer.bin.child = this._summaryBoxPointerItem.notificationStackWidget;
|
||||
this._summaryBoxPointerItem.closeButtonVisible = true;
|
||||
|
||||
let closeButton = this._summaryBoxPointerItem.closeButton;
|
||||
closeButton.show();
|
||||
this._summaryBoxPointerCloseClickedId = closeButton.connect('clicked', Lang.bind(this, this._hideSummaryBoxPointer));
|
||||
this._summaryBoxPointerItem.prepareNotificationStackForShowing();
|
||||
} else if (this._clickedSummaryItemMouseButton == 3) {
|
||||
this._summaryBoxPointer.bin.child = this._clickedSummaryItem.rightClickMenu;
|
||||
}
|
||||
|
||||
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
|
||||
untracked: true,
|
||||
grabFocus: true,
|
||||
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
|
||||
this._lock();
|
||||
@ -2418,6 +2485,21 @@ const MessageTray = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_onSummaryBoxPointerKeyPress: function(actor, event) {
|
||||
switch (event.get_key_symbol()) {
|
||||
case Clutter.KEY_Down:
|
||||
case Clutter.KEY_Escape:
|
||||
this._setClickedSummaryItem(null);
|
||||
this._updateState();
|
||||
return true;
|
||||
case Clutter.KEY_Delete:
|
||||
this._clickedSummaryItem.source.destroy();
|
||||
this._escapeTray();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onSummaryBoxPointerUngrabbed: function() {
|
||||
this._summaryBoxPointerState = State.HIDING;
|
||||
this._unlock();
|
||||
@ -2426,19 +2508,14 @@ const MessageTray = new Lang.Class({
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
this._hideSummaryBoxPointerCompleted();
|
||||
} else {
|
||||
if (global.stage.key_focus &&
|
||||
!this.actor.contains(global.stage.key_focus))
|
||||
this._setClickedSummaryItem(null);
|
||||
this._summaryBoxPointer.hide(BoxPointer.PopupAnimation.FULL, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
|
||||
}
|
||||
},
|
||||
|
||||
_hideSummaryBoxPointer: function() {
|
||||
// We should be sure to hide the box pointer if all notifications in it are destroyed while
|
||||
// it is hiding, so that we don't show an animation of an empty blob being hidden.
|
||||
if (this._summaryBoxPointerState == State.HIDING &&
|
||||
this._summaryBoxPointerItem.notificationStack.get_n_children() == 0) {
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this._grabHelper.ungrab({ actor: this._summaryBoxPointer.bin.child });
|
||||
},
|
||||
|
||||
@ -2449,7 +2526,9 @@ const MessageTray = new Lang.Class({
|
||||
this._summaryBoxPointer.bin.child = null;
|
||||
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
|
||||
this._summaryBoxPointerContentUpdatedId = 0;
|
||||
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerDoneDisplayingId);
|
||||
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
|
||||
this._summaryBoxPointerCloseClickedId = 0;
|
||||
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
|
||||
this._summaryBoxPointerDoneDisplayingId = 0;
|
||||
|
||||
let sourceNotificationStackDoneShowing = null;
|
||||
|
@ -20,7 +20,6 @@ const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||
const FADE_IN_BUTTONS_TIME = 0.33;
|
||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||
|
||||
const State = {
|
||||
@ -37,12 +36,13 @@ const ModalDialog = new Lang.Class({
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { shellReactive: false,
|
||||
styleClass: null,
|
||||
parentActor: Main.uiGroup
|
||||
});
|
||||
parentActor: Main.uiGroup,
|
||||
shouldFadeIn: true });
|
||||
|
||||
this.state = State.CLOSED;
|
||||
this._hasModal = false;
|
||||
this._shellReactive = params.shellReactive;
|
||||
this._shouldFadeIn = params.shouldFadeIn;
|
||||
|
||||
this._group = new St.Widget({ visible: false,
|
||||
x: 0,
|
||||
@ -116,8 +116,6 @@ const ModalDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
setButtons: function(buttons) {
|
||||
let hadChildren = this._buttonLayout.get_children() > 0;
|
||||
|
||||
this._buttonLayout.destroy_all_children();
|
||||
this._actionKeys = {};
|
||||
|
||||
@ -130,8 +128,11 @@ const ModalDialog = new Lang.Class({
|
||||
let key = buttonInfo['key'];
|
||||
let isDefault = buttonInfo['default'];
|
||||
|
||||
if (isDefault && !key)
|
||||
if (isDefault && !key) {
|
||||
this._actionKeys[Clutter.KEY_KP_Enter] = action;
|
||||
this._actionKeys[Clutter.KEY_ISO_Enter] = action;
|
||||
key = Clutter.KEY_Return;
|
||||
}
|
||||
|
||||
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
reactive: true,
|
||||
@ -165,21 +166,7 @@ const ModalDialog = new Lang.Class({
|
||||
this._actionKeys[key] = action;
|
||||
}
|
||||
|
||||
// Fade in buttons if there weren't any before
|
||||
if (!hadChildren && buttons.length > 0) {
|
||||
this._buttonLayout.opacity = 0;
|
||||
Tweener.addTween(this._buttonLayout,
|
||||
{ opacity: 255,
|
||||
time: FADE_IN_BUTTONS_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.emit('buttons-set');
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.emit('buttons-set');
|
||||
}
|
||||
|
||||
this.emit('buttons-set');
|
||||
},
|
||||
|
||||
_onKeyReleaseEvent: function(object, event) {
|
||||
@ -198,8 +185,11 @@ const ModalDialog = new Lang.Class({
|
||||
this.emit('destroy');
|
||||
},
|
||||
|
||||
_fadeOpen: function() {
|
||||
this._monitorConstraint.index = global.screen.get_current_monitor();
|
||||
_fadeOpen: function(onPrimary) {
|
||||
if (onPrimary)
|
||||
this._monitorConstraint.primary = true;
|
||||
else
|
||||
this._monitorConstraint.index = global.screen.get_current_monitor();
|
||||
|
||||
this.state = State.OPENING;
|
||||
|
||||
@ -210,7 +200,7 @@ const ModalDialog = new Lang.Class({
|
||||
this._group.show();
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
time: OPEN_AND_CLOSE_TIME,
|
||||
time: this._shouldFadeIn ? OPEN_AND_CLOSE_TIME : 0,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
@ -232,14 +222,14 @@ const ModalDialog = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
open: function(timestamp) {
|
||||
open: function(timestamp, onPrimary) {
|
||||
if (this.state == State.OPENED || this.state == State.OPENING)
|
||||
return true;
|
||||
|
||||
if (!this.pushModal(timestamp))
|
||||
return false;
|
||||
|
||||
this._fadeOpen();
|
||||
this._fadeOpen(onPrimary);
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -116,8 +116,8 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._busProxy = new Bus();
|
||||
|
||||
this._trayManager = new Shell.TrayManager();
|
||||
this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
|
||||
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
@ -356,12 +356,10 @@ const NotificationDaemon = new Lang.Class({
|
||||
ndata.actions, ndata.hints, ndata.notification];
|
||||
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
let iconActor = new St.Icon({ gicon: gicon,
|
||||
icon_size: MessageTray.NOTIFICATION_ICON_SIZE });
|
||||
|
||||
if (notification == null) {
|
||||
notification = new MessageTray.Notification(source, summary, body,
|
||||
{ icon: iconActor,
|
||||
{ gicon: gicon,
|
||||
bannerMarkup: true });
|
||||
ndata.notification = notification;
|
||||
notification.connect('destroy', Lang.bind(this,
|
||||
@ -386,7 +384,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
this._emitActionInvoked(ndata.id, actionId);
|
||||
}));
|
||||
} else {
|
||||
notification.update(summary, body, { icon: iconActor,
|
||||
notification.update(summary, body, { gicon: gicon,
|
||||
bannerMarkup: true,
|
||||
clear: true });
|
||||
}
|
||||
@ -499,7 +497,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
let wmClass = icon.wm_class.toLowerCase();
|
||||
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
|
||||
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
|
||||
return;
|
||||
|
||||
|
@ -10,6 +10,7 @@ const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
|
||||
const CenterLayout = imports.ui.centerLayout;
|
||||
const Dash = imports.ui.dash;
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
@ -23,9 +24,6 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||
// Time for initial animation going into Overview mode
|
||||
const ANIMATION_TIME = 0.25;
|
||||
|
||||
// We split the screen vertically between the dash and the view selector.
|
||||
const DASH_SPLIT_FRACTION = 0.1;
|
||||
|
||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
||||
|
||||
const SwipeScrollDirection = {
|
||||
@ -71,7 +69,6 @@ const ShellInfo = new Lang.Class({
|
||||
if (this._source.notifications.length == 0) {
|
||||
notification = new MessageTray.Notification(this._source, text, null);
|
||||
notification.setTransient(true);
|
||||
notification.setShowWhenLocked(true);
|
||||
} else {
|
||||
notification = this._source.notifications[0];
|
||||
notification.update(text, null, { clear: true });
|
||||
@ -92,16 +89,21 @@ const ShellInfo = new Lang.Class({
|
||||
const Overview = new Lang.Class({
|
||||
Name: 'Overview',
|
||||
|
||||
_init : function() {
|
||||
this.isDummy = !Main.sessionMode.hasOverview;
|
||||
_init: function() {
|
||||
this._overviewCreated = false;
|
||||
|
||||
// We only have an overview in user sessions, so
|
||||
// create a dummy overview in other cases
|
||||
if (this.isDummy) {
|
||||
this.animationInProgress = false;
|
||||
this.visible = false;
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_createOverview: function() {
|
||||
if (this._overviewCreated)
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
this._overviewCreated = true;
|
||||
|
||||
// The main BackgroundActor is inside global.window_group which is
|
||||
// hidden when displaying the overview, so we create a new
|
||||
@ -115,23 +117,18 @@ const Overview = new Lang.Class({
|
||||
this._desktopFade = new St.Bin();
|
||||
global.overlay_group.add_actor(this._desktopFade);
|
||||
|
||||
this._spacing = 0;
|
||||
|
||||
/* Translators: This is the main view to select
|
||||
activities. See also note for "Activities" string. */
|
||||
this._group = new St.Widget({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
reactive: true });
|
||||
this._group._delegate = this;
|
||||
this._group.connect('style-changed',
|
||||
Lang.bind(this, function() {
|
||||
let node = this._group.get_theme_node();
|
||||
let spacing = node.get_length('spacing');
|
||||
if (spacing != this._spacing) {
|
||||
this._spacing = spacing;
|
||||
this._relayout();
|
||||
}
|
||||
}));
|
||||
this._overview = new St.BoxLayout({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
reactive: true,
|
||||
vertical: true });
|
||||
this._overview._delegate = this;
|
||||
|
||||
let layout = new CenterLayout.CenterLayout();
|
||||
CenterLayout.connectSpacing(layout);
|
||||
this._group = new St.Widget({ name: 'overview-group',
|
||||
layout_manager: layout });
|
||||
|
||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
||||
this._scrollAdjustment = null;
|
||||
@ -144,18 +141,19 @@ const Overview = new Lang.Class({
|
||||
this._modal = false; // have a modal grab
|
||||
this.animationInProgress = false;
|
||||
this._hideInProgress = false;
|
||||
this.searchActive = false;
|
||||
this.appsActive = 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
|
||||
// Dash elements, or mouseover handlers in the workspaces.
|
||||
this._coverPane = new Clutter.Rectangle({ opacity: 0,
|
||||
reactive: true });
|
||||
this._group.add_actor(this._coverPane);
|
||||
this._overview.add_actor(this._coverPane);
|
||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
||||
|
||||
|
||||
this._group.hide();
|
||||
global.overlay_group.add_actor(this._group);
|
||||
this._overview.hide();
|
||||
global.overlay_group.add_actor(this._overview);
|
||||
|
||||
this._coverPane.hide();
|
||||
|
||||
@ -167,6 +165,8 @@ const Overview = new Lang.Class({
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
|
||||
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
|
||||
|
||||
this._windowSwitchTimeoutId = 0;
|
||||
this._windowSwitchTimestamp = 0;
|
||||
this._lastActiveWorkspaceIndex = -1;
|
||||
@ -174,6 +174,11 @@ const Overview = new Lang.Class({
|
||||
this._needsFakePointerEvent = false;
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
this.isDummy = !Main.sessionMode.hasOverview;
|
||||
this._createOverview();
|
||||
},
|
||||
|
||||
// The members we construct that are implemented in JS might
|
||||
// want to access the overview as Main.overview to connect
|
||||
// signal handlers and so forth. So we create them after
|
||||
@ -184,6 +189,13 @@ const Overview = new Lang.Class({
|
||||
|
||||
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.actor }),
|
||||
reactive: false,
|
||||
opacity: 0 });
|
||||
this._overview.add_actor(this._panelGhost);
|
||||
|
||||
this._searchEntry = new St.Entry({ name: 'searchEntry',
|
||||
/* Translators: this is the text displayed
|
||||
in the search entry when no search is
|
||||
@ -192,16 +204,13 @@ const Overview = new Lang.Class({
|
||||
hint_text: _("Type to search..."),
|
||||
track_hover: true,
|
||||
can_focus: true });
|
||||
this._group.add_actor(this._searchEntry);
|
||||
|
||||
this._dash = new Dash.Dash();
|
||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
||||
this._dash.showAppsButton);
|
||||
this._group.add_actor(this._viewSelector.actor);
|
||||
this._group.add_actor(this._dash.actor);
|
||||
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
|
||||
x_align: St.Align.MIDDLE });
|
||||
this._overview.add_actor(this._searchEntryBin);
|
||||
|
||||
// TODO - recalculate everything when desktop size changes
|
||||
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
|
||||
this._dash = new Dash.Dash();
|
||||
this._group.add_actor(this._dash.actor);
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
this._dash.connect('icon-size-changed',
|
||||
Lang.bind(this, function() {
|
||||
@ -212,6 +221,82 @@ const Overview = new Lang.Class({
|
||||
// the left of the overview
|
||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
|
||||
|
||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
||||
this._dash.showAppsButton);
|
||||
this._group.add_actor(this._viewSelector.actor);
|
||||
|
||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||
this._group.add_actor(this._thumbnailsBox.actor);
|
||||
|
||||
// TODO: unique icon
|
||||
Main.ctrlAltTabManager.addGroup(this._thumbnailsBox.actor, _("Workspaces"), 'user-bookmarks-symbolic');
|
||||
|
||||
// Add our same-line elements after the search entry
|
||||
this._overview.add_actor(this._group);
|
||||
|
||||
// Then account for message tray
|
||||
this._messageTrayGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.messageTray.actor }),
|
||||
reactive: false,
|
||||
opacity: 0 });
|
||||
this._overview.add_actor(this._messageTrayGhost);
|
||||
|
||||
this._viewSelector.connect('search-begin', Lang.bind(this,
|
||||
function() {
|
||||
this.searchActive = true;
|
||||
this._dash.hide();
|
||||
this._thumbnailsBox.hide();
|
||||
Main.messageTray.hide();
|
||||
}));
|
||||
this._viewSelector.connect('search-cancelled', Lang.bind(this,
|
||||
function() {
|
||||
this.searchActive = false;
|
||||
this._dash.show();
|
||||
this._thumbnailsBox.show();
|
||||
// search-cancelled is emitted if we leave the overview
|
||||
// directly from searching. That means the message tray will
|
||||
// be shown when we reach the workspace. Don't do this, only
|
||||
// show the message tray on search-cancelled if we are still
|
||||
// in the overview/not transitioning out of it.
|
||||
if (this.visible && !this.animationInProgress)
|
||||
Main.messageTray.show();
|
||||
}));
|
||||
this._viewSelector.connect('apps-button-checked', Lang.bind(this,
|
||||
function(vs, checked) {
|
||||
this.appsActive = checked;
|
||||
if (checked) {
|
||||
this._thumbnailsBox.hide();
|
||||
Main.messageTray.hide();
|
||||
} else {
|
||||
this._thumbnailsBox.show();
|
||||
if (this.visible && !this.animationInProgress)
|
||||
Main.messageTray.show();
|
||||
}
|
||||
}));
|
||||
|
||||
this.connect('app-drag-begin',
|
||||
Lang.bind(this, function () {
|
||||
this._dash.show();
|
||||
this._thumbnailsBox.show();
|
||||
}));
|
||||
this.connect('app-drag-cancelled',
|
||||
Lang.bind(this, function () {
|
||||
if (this.searchActive) {
|
||||
this._dash.hide();
|
||||
this._thumbnailsBox.hide();
|
||||
} else if (this.appsActive) {
|
||||
this._thumbnailsBox.hide();
|
||||
}
|
||||
}));
|
||||
this.connect('app-drag-end',
|
||||
Lang.bind(this, function () {
|
||||
if (this.searchActive) {
|
||||
this._dash.hide();
|
||||
this._thumbnailsBox.hide();
|
||||
} else if (this.appsActive) {
|
||||
this._thumbnailsBox.hide();
|
||||
}
|
||||
}));
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||
this._relayout();
|
||||
},
|
||||
@ -461,7 +546,9 @@ const Overview = new Lang.Class({
|
||||
if (windows.length == 0)
|
||||
return null;
|
||||
|
||||
let clone = new Clutter.Clone({ source: windows[0].get_texture() });
|
||||
let window = windows[0];
|
||||
let clone = new Clutter.Clone({ source: window.get_texture(),
|
||||
x: window.x, y: window.y });
|
||||
clone.source.connect('destroy', Lang.bind(this, function() {
|
||||
clone.destroy();
|
||||
}));
|
||||
@ -475,55 +562,41 @@ const Overview = new Lang.Class({
|
||||
this.hide();
|
||||
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
let contentY = Main.panel.actor.height;
|
||||
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
|
||||
|
||||
this._group.set_position(primary.x, primary.y);
|
||||
this._group.set_size(primary.width, primary.height);
|
||||
this._overview.set_position(primary.x, primary.y);
|
||||
this._overview.set_size(primary.width, primary.height);
|
||||
|
||||
this._coverPane.set_position(0, contentY);
|
||||
this._coverPane.set_size(primary.width, contentHeight);
|
||||
},
|
||||
|
||||
let searchWidth = this._searchEntry.get_width();
|
||||
let searchHeight = this._searchEntry.get_height();
|
||||
let searchX = (primary.width - searchWidth) / 2;
|
||||
let searchY = contentY + this._spacing;
|
||||
_onRestacked: function() {
|
||||
let stack = global.get_window_actors();
|
||||
let stackIndices = {};
|
||||
|
||||
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
|
||||
let dashY = searchY + searchHeight + this._spacing;
|
||||
let dashX;
|
||||
if (rtl) {
|
||||
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
dashX = primary.width;
|
||||
} else {
|
||||
dashX = 0;
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
// Use the stable sequence for an integer to use as a hash key
|
||||
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||
}
|
||||
|
||||
let viewX = rtl ? 0 : dashWidth + this._spacing;
|
||||
let viewY = searchY + searchHeight + this._spacing;
|
||||
let viewWidth = primary.width - dashWidth - this._spacing;
|
||||
let viewHeight = contentHeight - this._spacing - viewY;
|
||||
|
||||
this._searchEntry.set_position(searchX, searchY);
|
||||
this._dash.actor.set_position(dashX, dashY);
|
||||
this._viewSelector.actor.set_position(viewX, viewY);
|
||||
this._viewSelector.actor.set_size(viewWidth, viewHeight);
|
||||
this.emit('sync-window-stacking', stackIndices);
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
beginItemDrag: function(source) {
|
||||
this.emit('item-drag-begin');
|
||||
beginAppDrag: function(source) {
|
||||
this.emit('app-drag-begin');
|
||||
},
|
||||
|
||||
cancelledItemDrag: function(source) {
|
||||
this.emit('item-drag-cancelled');
|
||||
cancelledAppDrag: function(source) {
|
||||
this.emit('app-drag-cancelled');
|
||||
},
|
||||
|
||||
endItemDrag: function(source) {
|
||||
this.emit('item-drag-end');
|
||||
endAppDrag: function(source) {
|
||||
this.emit('app-drag-end');
|
||||
},
|
||||
|
||||
beginWindowDrag: function(source) {
|
||||
@ -547,13 +620,13 @@ const Overview = new Lang.Class({
|
||||
if (this._shown)
|
||||
return;
|
||||
// Do this manually instead of using _syncInputMode, to handle failure
|
||||
if (!Main.pushModal(this._group))
|
||||
if (!Main.pushModal(this._overview))
|
||||
return;
|
||||
this._modal = true;
|
||||
this._animateVisible();
|
||||
this._shown = true;
|
||||
|
||||
this._buttonPressId = this._group.connect('button-press-event',
|
||||
this._buttonPressId = this._overview.connect('button-press-event',
|
||||
Lang.bind(this, this._onButtonPress));
|
||||
},
|
||||
|
||||
@ -597,12 +670,12 @@ const Overview = new Lang.Class({
|
||||
// Disable unredirection while in the overview
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
global.window_group.hide();
|
||||
this._group.show();
|
||||
this._overview.show();
|
||||
this._background.show();
|
||||
this._viewSelector.show();
|
||||
|
||||
this._group.opacity = 0;
|
||||
Tweener.addTween(this._group,
|
||||
this._overview.opacity = 0;
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
@ -656,7 +729,7 @@ const Overview = new Lang.Class({
|
||||
this._syncInputMode();
|
||||
|
||||
if (this._buttonPressId > 0)
|
||||
this._group.disconnect(this._buttonPressId);
|
||||
this._overview.disconnect(this._buttonPressId);
|
||||
this._buttonPressId = 0;
|
||||
},
|
||||
|
||||
@ -698,20 +771,20 @@ const Overview = new Lang.Class({
|
||||
|
||||
if (this._shown) {
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._group))
|
||||
if (Main.pushModal(this._overview))
|
||||
this._modal = true;
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
} else if (this._shownTemporarily) {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._group);
|
||||
Main.popModal(this._overview);
|
||||
this._modal = false;
|
||||
}
|
||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
||||
} else {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._group);
|
||||
Main.popModal(this._overview);
|
||||
this._modal = false;
|
||||
}
|
||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
||||
@ -729,7 +802,7 @@ const Overview = new Lang.Class({
|
||||
this._viewSelector.zoomFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._group,
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
@ -771,7 +844,7 @@ const Overview = new Lang.Class({
|
||||
this._viewSelector.hide();
|
||||
this._desktopFade.hide();
|
||||
this._background.hide();
|
||||
this._group.hide();
|
||||
this._overview.hide();
|
||||
|
||||
this.visible = false;
|
||||
this.animationInProgress = false;
|
||||
|
237
js/ui/panel.js
237
js/ui/panel.js
@ -15,6 +15,7 @@ const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
|
||||
const CenterLayout = imports.ui.centerLayout;
|
||||
const Config = imports.misc.config;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const DND = imports.ui.dnd;
|
||||
@ -247,6 +248,10 @@ const AppMenuButton = new Lang.Class({
|
||||
this._container.connect('get-preferred-height', Lang.bind(this, this._getContentPreferredHeight));
|
||||
this._container.connect('allocate', Lang.bind(this, this._contentAllocate));
|
||||
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
textureCache.connect('icon-theme-changed',
|
||||
Lang.bind(this, this._onIconThemeChanged));
|
||||
|
||||
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
|
||||
this._iconBox.connect('style-changed',
|
||||
Lang.bind(this, this._onIconBoxStyleChanged));
|
||||
@ -332,6 +337,15 @@ const AppMenuButton = new Lang.Class({
|
||||
this._updateIconBoxClip();
|
||||
},
|
||||
|
||||
_onIconThemeChanged: function() {
|
||||
if (this._iconBox.child == null)
|
||||
return;
|
||||
|
||||
this._iconBox.child.destroy();
|
||||
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
this._iconBox.set_child(icon);
|
||||
},
|
||||
|
||||
_updateIconBoxClip: function() {
|
||||
let allocation = this._iconBox.allocation;
|
||||
if (this._iconBottomClip > 0)
|
||||
@ -468,15 +482,6 @@ const AppMenuButton = new Lang.Class({
|
||||
this._sync();
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
if (locked) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
this._sync();
|
||||
}
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
let focusedApp = tracker.focus_app;
|
||||
@ -610,8 +615,8 @@ const ActivitiesButton = new Lang.Class({
|
||||
|
||||
this.actor.label_actor = this._label;
|
||||
|
||||
this._hotCorner = new Layout.HotCorner();
|
||||
container.add_actor(this._hotCorner.actor);
|
||||
this.hotCorner = new Layout.HotCorner();
|
||||
container.add_actor(this.hotCorner.actor);
|
||||
|
||||
// Hack up our menu...
|
||||
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
|
||||
@ -661,10 +666,10 @@ const ActivitiesButton = new Lang.Class({
|
||||
}
|
||||
|
||||
hotBox.x1 = Math.round(x);
|
||||
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
|
||||
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
|
||||
hotBox.y1 = Math.round(y);
|
||||
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
|
||||
this._hotCorner.actor.allocate(hotBox, flags);
|
||||
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
|
||||
this.hotCorner.actor.allocate(hotBox, flags);
|
||||
},
|
||||
|
||||
handleDragOver: function(source, actor, x, y, time) {
|
||||
@ -686,7 +691,7 @@ const ActivitiesButton = new Lang.Class({
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||
if (!this._hotCorner.shouldToggleOverviewOnClick())
|
||||
if (!this.hotCorner.shouldToggleOverviewOnClick())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -910,6 +915,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
||||
'logo': imports.gdm.loginDialog.LogoMenuButton,
|
||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
||||
'userMenu': imports.ui.userMenu.UserMenuButton
|
||||
@ -926,12 +932,47 @@ try {
|
||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
||||
}
|
||||
|
||||
const PanelLayout = new Lang.Class({
|
||||
Name: 'PanelLayout',
|
||||
Extends: CenterLayout.CenterLayout,
|
||||
|
||||
vfunc_allocate: function(container, box, flags) {
|
||||
this.parent(container, box, flags);
|
||||
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let [left, center, right, leftCorner, rightCorner] = container.get_children();
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
let cornerMinWidth, cornerMinHeight;
|
||||
let cornerWidth, cornerHeight;
|
||||
|
||||
[cornerMinWidth, cornerWidth] = leftCorner.get_preferred_width(-1);
|
||||
[cornerMinHeight, cornerHeight] = leftCorner.get_preferred_height(-1);
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = cornerWidth;
|
||||
childBox.y1 = availHeight;
|
||||
childBox.y2 = availHeight + cornerHeight;
|
||||
leftCorner.allocate(childBox, flags);
|
||||
|
||||
[cornerMinWidth, cornerWidth] = rightCorner.get_preferred_width(-1);
|
||||
[cornerMinHeight, cornerHeight] = rightCorner.get_preferred_height(-1);
|
||||
childBox.x1 = availWidth - cornerWidth;
|
||||
childBox.x2 = availWidth;
|
||||
childBox.y1 = availHeight;
|
||||
childBox.y2 = availHeight + cornerHeight;
|
||||
rightCorner.allocate(childBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const Panel = new Lang.Class({
|
||||
Name: 'Panel',
|
||||
|
||||
_init : function() {
|
||||
this.actor = new Shell.GenericContainer({ name: 'panel',
|
||||
reactive: true });
|
||||
this.actor = new St.Widget({ name: 'panel',
|
||||
reactive: true,
|
||||
layoutManager: new PanelLayout() });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.statusArea = {};
|
||||
@ -943,8 +984,6 @@ const Panel = new Lang.Class({
|
||||
this.actor.remove_style_class_name('in-overview');
|
||||
}));
|
||||
|
||||
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._onLockStateChanged));
|
||||
|
||||
this.menuManager = new PopupMenu.PopupMenuManager(this);
|
||||
|
||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||
@ -958,7 +997,6 @@ const Panel = new Lang.Class({
|
||||
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
|
||||
else
|
||||
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
|
||||
|
||||
this.actor.add_actor(this._leftCorner.actor);
|
||||
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
@ -967,88 +1005,14 @@ const Panel = new Lang.Class({
|
||||
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
|
||||
this.actor.add_actor(this._rightCorner.actor);
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
|
||||
Main.layoutManager.panelBox.add(this.actor);
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here-symbolic',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
alloc.min_size = -1;
|
||||
alloc.natural_size = Main.layoutManager.primaryMonitor.width;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
// We don't need to implement this; it's forced by the CSS
|
||||
alloc.min_size = -1;
|
||||
alloc.natural_size = -1;
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let allocWidth = box.x2 - box.x1;
|
||||
let allocHeight = box.y2 - box.y1;
|
||||
|
||||
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
|
||||
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
|
||||
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
|
||||
|
||||
let sideWidth, centerWidth;
|
||||
centerWidth = centerNaturalWidth;
|
||||
sideWidth = (allocWidth - centerWidth) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = allocHeight;
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
||||
leftNaturalWidth);
|
||||
childBox.x2 = allocWidth;
|
||||
} else {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = Math.min(Math.floor(sideWidth),
|
||||
leftNaturalWidth);
|
||||
}
|
||||
this._leftBox.allocate(childBox, flags);
|
||||
|
||||
childBox.x1 = Math.ceil(sideWidth);
|
||||
childBox.y1 = 0;
|
||||
childBox.x2 = childBox.x1 + centerWidth;
|
||||
childBox.y2 = allocHeight;
|
||||
this._centerBox.allocate(childBox, flags);
|
||||
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = allocHeight;
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = Math.min(Math.floor(sideWidth),
|
||||
rightNaturalWidth);
|
||||
} else {
|
||||
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
|
||||
rightNaturalWidth);
|
||||
childBox.x2 = allocWidth;
|
||||
}
|
||||
this._rightBox.allocate(childBox, flags);
|
||||
|
||||
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
|
||||
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = cornerWidth;
|
||||
childBox.y1 = allocHeight;
|
||||
childBox.y2 = allocHeight + cornerHeight;
|
||||
this._leftCorner.actor.allocate(childBox, flags);
|
||||
|
||||
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
|
||||
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
|
||||
childBox.x1 = allocWidth - cornerWidth;
|
||||
childBox.x2 = allocWidth;
|
||||
childBox.y1 = allocHeight;
|
||||
childBox.y2 = allocHeight + cornerHeight;
|
||||
this._rightCorner.actor.allocate(childBox, flags);
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
|
||||
this._updatePanel();
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
@ -1103,11 +1067,27 @@ const Panel = new Lang.Class({
|
||||
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
init: function() {
|
||||
set boxOpacity(value) {
|
||||
let isReactive = value > 0;
|
||||
|
||||
this._leftBox.opacity = value;
|
||||
this._leftBox.reactive = isReactive;
|
||||
this._centerBox.opacity = value;
|
||||
this._centerBox.reactive = isReactive;
|
||||
this._rightBox.opacity = value;
|
||||
this._rightBox.reactive = isReactive;
|
||||
},
|
||||
|
||||
get boxOpacity() {
|
||||
return this._leftBox.opacity;
|
||||
},
|
||||
|
||||
_updatePanel: function() {
|
||||
let panel = Main.sessionMode.panel;
|
||||
this._initBox(panel.left, this._leftBox);
|
||||
this._initBox(panel.center, this._centerBox);
|
||||
this._initBox(panel.right, this._rightBox);
|
||||
this._hideIndicators();
|
||||
this._updateBox(panel.left, this._leftBox);
|
||||
this._updateBox(panel.center, this._centerBox);
|
||||
this._updateBox(panel.right, this._rightBox);
|
||||
},
|
||||
|
||||
_initBox: function(elements, box) {
|
||||
@ -1119,20 +1099,63 @@ const Panel = new Lang.Class({
|
||||
// bluetooth or network)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
let indicator = new constructor(this);
|
||||
this._addToPanelBox(role, indicator, i, box);
|
||||
_hideIndicators: function() {
|
||||
for (let role in PANEL_ITEM_IMPLEMENTATIONS) {
|
||||
let indicator = this.statusArea[role];
|
||||
if (!indicator)
|
||||
continue;
|
||||
if (indicator.menu)
|
||||
indicator.menu.close();
|
||||
indicator.container.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_ensureIndicator: function(role) {
|
||||
let indicator = this.statusArea[role];
|
||||
if (!indicator) {
|
||||
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
||||
if (!constructor) {
|
||||
// This icon is not implemented (this is a bug)
|
||||
return null;
|
||||
}
|
||||
indicator = new constructor(this);
|
||||
this.statusArea[role] = indicator;
|
||||
}
|
||||
return indicator;
|
||||
},
|
||||
|
||||
_updateBox: function(elements, box) {
|
||||
let nChildren = box.get_n_children();
|
||||
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let role = elements[i];
|
||||
let indicator = this._ensureIndicator(role);
|
||||
if (indicator == null)
|
||||
continue;
|
||||
|
||||
this._addToPanelBox(role, indicator, i + nChildren, box);
|
||||
}
|
||||
},
|
||||
|
||||
_addToPanelBox: function(role, indicator, position, box) {
|
||||
box.insert_child_at_index(indicator.actor, position);
|
||||
let container = indicator.container;
|
||||
container.show();
|
||||
|
||||
let parent = container.get_parent();
|
||||
if (parent)
|
||||
parent.remove_actor(container);
|
||||
|
||||
box.insert_child_at_index(container, position);
|
||||
if (indicator.menu)
|
||||
this.menuManager.addMenu(indicator.menu);
|
||||
this.statusArea[role] = indicator;
|
||||
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
|
||||
delete this.statusArea[role];
|
||||
emitter.disconnect(destroyId);
|
||||
container.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
@ -1150,12 +1173,8 @@ const Panel = new Lang.Class({
|
||||
right: this._rightBox
|
||||
};
|
||||
let boxContainer = boxes[box] || this._rightBox;
|
||||
this.statusArea[role] = indicator;
|
||||
this._addToPanelBox(role, indicator, position, boxContainer);
|
||||
return indicator;
|
||||
},
|
||||
|
||||
_onLockStateChanged: function(shield, locked) {
|
||||
for (let id in this.statusArea)
|
||||
this.statusArea[id].setLockedState(locked);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
@ -21,6 +21,10 @@ const ButtonBox = new Lang.Class({
|
||||
this.actor = new Shell.GenericContainer(params);
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.container = new St.Bin({ y_fill: true,
|
||||
x_fill: true,
|
||||
child: this.actor });
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
@ -115,6 +119,12 @@ const Button = new Lang.Class({
|
||||
this.setName(nameText);
|
||||
},
|
||||
|
||||
setSensitive: function(sensitive) {
|
||||
this.actor.reactive = sensitive;
|
||||
this.actor.can_focus = sensitive;
|
||||
this.actor.track_hover = sensitive;
|
||||
},
|
||||
|
||||
setName: function(text) {
|
||||
if (text != null) {
|
||||
// This is the easiest way to provide a accessible name to
|
||||
@ -146,13 +156,6 @@ const Button = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
// default behaviour is to hide completely
|
||||
if (locked)
|
||||
this.menu.close();
|
||||
this.actor.visible = !locked;
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
if (!this.menu)
|
||||
return;
|
||||
@ -211,7 +214,8 @@ const Button = new Lang.Class({
|
||||
destroy: function() {
|
||||
this.actor._delegate = null;
|
||||
|
||||
this.menu.destroy();
|
||||
if (this.menu)
|
||||
this.menu.destroy();
|
||||
this.actor.destroy();
|
||||
|
||||
this.emit('destroy');
|
||||
|
@ -1,412 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
/**
|
||||
* Represents a place object, which is most normally a bookmark entry,
|
||||
* a mount/volume, or a special place like the Home Folder, Computer, and Network.
|
||||
*
|
||||
* @name: String title
|
||||
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
|
||||
* @launch: A JavaScript callback to launch the entry
|
||||
*/
|
||||
const PlaceInfo = new Lang.Class({
|
||||
Name: 'PlaceInfo',
|
||||
|
||||
_init: function(id, name, iconFactory, launch) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this._lowerName = name.toLowerCase();
|
||||
this.iconFactory = iconFactory;
|
||||
this.launch = launch;
|
||||
},
|
||||
|
||||
matchTerms: function(terms) {
|
||||
let mtype = Search.MatchType.NONE;
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
let idx = this._lowerName.indexOf(term);
|
||||
if (idx == 0) {
|
||||
mtype = Search.MatchType.PREFIX;
|
||||
} else if (idx > 0) {
|
||||
if (mtype == Search.MatchType.NONE)
|
||||
mtype = Search.MatchType.SUBSTRING;
|
||||
} else {
|
||||
return Search.MatchType.NONE;
|
||||
}
|
||||
}
|
||||
return mtype;
|
||||
},
|
||||
|
||||
isRemovable: function() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function to translate launch parameters into a GAppLaunchContext
|
||||
function _makeLaunchContext(params)
|
||||
{
|
||||
params = Params.parse(params, { workspace: -1,
|
||||
timestamp: 0 });
|
||||
|
||||
let launchContext = global.create_app_launch_context();
|
||||
if (params.workspace != -1)
|
||||
launchContext.set_desktop(params.workspace);
|
||||
if (params.timestamp != 0)
|
||||
launchContext.set_timestamp(params.timestamp);
|
||||
|
||||
return launchContext;
|
||||
}
|
||||
|
||||
const PlaceDeviceInfo = new Lang.Class({
|
||||
Name: 'PlaceDeviceInfo',
|
||||
Extends: PlaceInfo,
|
||||
|
||||
_init: function(mount) {
|
||||
this._mount = mount;
|
||||
this.name = mount.get_name();
|
||||
this._lowerName = this.name.toLowerCase();
|
||||
this.id = 'mount:' + mount.get_root().get_uri();
|
||||
},
|
||||
|
||||
iconFactory: function(size) {
|
||||
let icon = this._mount.get_icon();
|
||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
||||
},
|
||||
|
||||
launch: function(params) {
|
||||
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
|
||||
_makeLaunchContext(params));
|
||||
},
|
||||
|
||||
isRemovable: function() {
|
||||
return this._mount.can_unmount();
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
if (!this.isRemovable())
|
||||
return;
|
||||
|
||||
if (this._mount.can_eject())
|
||||
this._mount.eject(0, null, Lang.bind(this, this._removeFinish));
|
||||
else
|
||||
this._mount.unmount(0, null, Lang.bind(this, this._removeFinish));
|
||||
},
|
||||
|
||||
_removeFinish: function(o, res, data) {
|
||||
try {
|
||||
if (this._mount.can_eject())
|
||||
this._mount.eject_finish(res);
|
||||
else
|
||||
this._mount.unmount_finish(res);
|
||||
} catch (e) {
|
||||
let message = _("Failed to unmount '%s'").format(o.get_name());
|
||||
Main.overview.setMessage(message,
|
||||
Lang.bind(this, this.remove),
|
||||
_("Retry"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const PlacesManager = new Lang.Class({
|
||||
Name: 'PlacesManager',
|
||||
|
||||
_init: function() {
|
||||
this._defaultPlaces = [];
|
||||
this._mounts = [];
|
||||
this._bookmarks = [];
|
||||
|
||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||
let homeUri = homeFile.get_uri();
|
||||
let homeLabel = Shell.util_get_label_for_uri (homeUri);
|
||||
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
|
||||
this._home = new PlaceInfo('special:home', homeLabel,
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
|
||||
});
|
||||
|
||||
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
||||
let desktopFile = Gio.file_new_for_path (desktopPath);
|
||||
let desktopUri = desktopFile.get_uri();
|
||||
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
|
||||
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
|
||||
this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
|
||||
});
|
||||
|
||||
this._defaultPlaces.push(this._home);
|
||||
this._defaultPlaces.push(this._desktopMenu);
|
||||
|
||||
/*
|
||||
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
||||
*/
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
|
||||
this._updateDevices();
|
||||
|
||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']);
|
||||
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
||||
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||
this._bookmarkTimeoutId = 0;
|
||||
this._monitor.connect('changed', Lang.bind(this, function () {
|
||||
if (this._bookmarkTimeoutId > 0)
|
||||
return;
|
||||
/* Defensive event compression */
|
||||
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
||||
this._bookmarkTimeoutId = 0;
|
||||
this._reloadBookmarks();
|
||||
return false;
|
||||
}));
|
||||
}));
|
||||
|
||||
this._reloadBookmarks();
|
||||
},
|
||||
|
||||
_updateDevices: function() {
|
||||
this._mounts = [];
|
||||
|
||||
/* first go through all connected drives */
|
||||
let drives = this._volumeMonitor.get_connected_drives();
|
||||
for (let i = 0; i < drives.length; i++) {
|
||||
let volumes = drives[i].get_volumes();
|
||||
for(let j = 0; j < volumes.length; j++) {
|
||||
let mount = volumes[j].get_mount();
|
||||
if(mount != null) {
|
||||
this._addMount(mount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add all volumes that is not associated with a drive */
|
||||
let volumes = this._volumeMonitor.get_volumes();
|
||||
for(let i = 0; i < volumes.length; i++) {
|
||||
if(volumes[i].get_drive() != null)
|
||||
continue;
|
||||
|
||||
let mount = volumes[i].get_mount();
|
||||
if(mount != null) {
|
||||
this._addMount(mount);
|
||||
}
|
||||
}
|
||||
|
||||
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
|
||||
let mounts = this._volumeMonitor.get_mounts();
|
||||
for(let i = 0; i < mounts.length; i++) {
|
||||
if(mounts[i].is_shadowed())
|
||||
continue;
|
||||
|
||||
if(mounts[i].get_volume())
|
||||
continue;
|
||||
|
||||
this._addMount(mounts[i]);
|
||||
}
|
||||
|
||||
/* We emit two signals, one for a generic 'all places' update
|
||||
* and the other for one specific to mounts. We do this because
|
||||
* clients like PlaceDisplay may only care about places in general
|
||||
* being updated while clients like DashPlaceDisplay care which
|
||||
* specific type of place got updated.
|
||||
*/
|
||||
this.emit('mounts-updated');
|
||||
this.emit('places-updated');
|
||||
|
||||
},
|
||||
|
||||
_reloadBookmarks: function() {
|
||||
|
||||
this._bookmarks = [];
|
||||
|
||||
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
|
||||
return;
|
||||
|
||||
let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
|
||||
|
||||
let bookmarks = bookmarksContent.split('\n');
|
||||
|
||||
let bookmarksToLabel = {};
|
||||
let bookmarksOrder = [];
|
||||
for (let i = 0; i < bookmarks.length; i++) {
|
||||
let bookmarkLine = bookmarks[i];
|
||||
let components = bookmarkLine.split(' ');
|
||||
let bookmark = components[0];
|
||||
if (bookmark in bookmarksToLabel)
|
||||
continue;
|
||||
let label = null;
|
||||
if (components.length > 1)
|
||||
label = components.slice(1).join(' ');
|
||||
bookmarksToLabel[bookmark] = label;
|
||||
bookmarksOrder.push(bookmark);
|
||||
}
|
||||
|
||||
for (let i = 0; i < bookmarksOrder.length; i++) {
|
||||
let bookmark = bookmarksOrder[i];
|
||||
let label = bookmarksToLabel[bookmark];
|
||||
let file = Gio.file_new_for_uri(bookmark);
|
||||
if (!file.query_exists(null))
|
||||
continue;
|
||||
if (label == null)
|
||||
label = Shell.util_get_label_for_uri(bookmark);
|
||||
if (label == null)
|
||||
continue;
|
||||
let icon = Shell.util_get_icon_for_uri(bookmark);
|
||||
|
||||
let item = new PlaceInfo('bookmark:' + bookmark, label,
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
|
||||
});
|
||||
this._bookmarks.push(item);
|
||||
}
|
||||
|
||||
/* See comment in _updateDevices for explanation why there are two signals. */
|
||||
this.emit('bookmarks-updated');
|
||||
this.emit('places-updated');
|
||||
},
|
||||
|
||||
_addMount: function(mount) {
|
||||
let devItem = new PlaceDeviceInfo(mount);
|
||||
this._mounts.push(devItem);
|
||||
},
|
||||
|
||||
getAllPlaces: function () {
|
||||
return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts());
|
||||
},
|
||||
|
||||
getDefaultPlaces: function () {
|
||||
return this._defaultPlaces;
|
||||
},
|
||||
|
||||
getBookmarks: function () {
|
||||
return this._bookmarks;
|
||||
},
|
||||
|
||||
getMounts: function () {
|
||||
return this._mounts;
|
||||
},
|
||||
|
||||
_lookupIndexById: function(sourceArray, id) {
|
||||
for (let i = 0; i < sourceArray.length; i++) {
|
||||
let place = sourceArray[i];
|
||||
if (place.id == id)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
lookupPlaceById: function(id) {
|
||||
let colonIdx = id.indexOf(':');
|
||||
let type = id.substring(0, colonIdx);
|
||||
let sourceArray = null;
|
||||
if (type == 'special')
|
||||
sourceArray = this._defaultPlaces;
|
||||
else if (type == 'mount')
|
||||
sourceArray = this._mounts;
|
||||
else if (type == 'bookmark')
|
||||
sourceArray = this._bookmarks;
|
||||
return sourceArray[this._lookupIndexById(sourceArray, id)];
|
||||
},
|
||||
|
||||
_removeById: function(sourceArray, id) {
|
||||
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(PlacesManager.prototype);
|
||||
|
||||
const PlaceSearchProvider = new Lang.Class({
|
||||
Name: 'PlaceSearchProvider',
|
||||
Extends: Search.SearchProvider,
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("PLACES & DEVICES"));
|
||||
this.placesManager = new PlacesManager();
|
||||
},
|
||||
|
||||
getResultMetas: function(resultIds, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < resultIds.length; i++) {
|
||||
let placeInfo = this.placesManager.lookupPlaceById(resultIds[i]);
|
||||
if (!placeInfo)
|
||||
metas.push(null);
|
||||
else
|
||||
metas.push({ 'id': resultIds[i],
|
||||
'name': placeInfo.name,
|
||||
'createIcon': function(size) {
|
||||
return placeInfo.iconFactory(size);
|
||||
}
|
||||
});
|
||||
}
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
let placeInfo = this.placesManager.lookupPlaceById(id);
|
||||
placeInfo.launch(params);
|
||||
},
|
||||
|
||||
_compareResultMeta: function (idA, idB) {
|
||||
let infoA = this.placesManager.lookupPlaceById(idA);
|
||||
let infoB = this.placesManager.lookupPlaceById(idB);
|
||||
return infoA.name.localeCompare(infoB.name);
|
||||
},
|
||||
|
||||
_searchPlaces: function(places, terms) {
|
||||
let prefixResults = [];
|
||||
let substringResults = [];
|
||||
|
||||
terms = terms.map(String.toLowerCase);
|
||||
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
let place = places[i];
|
||||
let mtype = place.matchTerms(terms);
|
||||
if (mtype == Search.MatchType.PREFIX)
|
||||
prefixResults.push(place.id);
|
||||
else if (mtype == Search.MatchType.SUBSTRING)
|
||||
substringResults.push(place.id);
|
||||
}
|
||||
prefixResults.sort(Lang.bind(this, this._compareResultMeta));
|
||||
substringResults.sort(Lang.bind(this, this._compareResultMeta));
|
||||
|
||||
this.searchSystem.pushResults(this, prefixResults.concat(substringResults));
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
let places = this.placesManager.getAllPlaces();
|
||||
this._searchPlaces(places, terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
let places = previousResults.map(Lang.bind(this, function(id) {
|
||||
return this.placesManager.lookupPlaceById(id);
|
||||
}));
|
||||
this._searchPlaces(places, terms);
|
||||
}
|
||||
});
|
@ -2,6 +2,7 @@
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
// We stop polling if the user is idle for more than this amount of time
|
||||
@ -40,9 +41,9 @@ const PointerWatcher = new Lang.Class({
|
||||
Name: 'PointerWatcher',
|
||||
|
||||
_init: function() {
|
||||
let idleMonitor = Shell.IdleMonitor.get();
|
||||
idleMonitor.add_watch(IDLE_TIME,
|
||||
Lang.bind(this, this._onIdleMonitorWatch));
|
||||
let idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||
idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
|
||||
this._watches = [];
|
||||
this.pointerX = null;
|
||||
@ -78,11 +79,14 @@ const PointerWatcher = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_onIdleMonitorWatch: function(monitor, id, userBecameIdle) {
|
||||
this._idle = userBecameIdle;
|
||||
if (!userBecameIdle)
|
||||
this._updatePointer();
|
||||
_onIdleMonitorBecameActive: function(monitor) {
|
||||
this._idle = false;
|
||||
this._updatePointer();
|
||||
this._updateTimeout();
|
||||
},
|
||||
|
||||
_onIdleMonitorBecameIdle: function(monitor) {
|
||||
this._idle = true;
|
||||
this._updateTimeout();
|
||||
},
|
||||
|
||||
|
@ -61,6 +61,9 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
|
||||
this.setSensitive(this.sensitive);
|
||||
|
||||
if (!this._activatable)
|
||||
this.actor.add_style_class_name('popup-inactive-menu-item');
|
||||
|
||||
if (params.style_class)
|
||||
this.actor.add_style_class_name(params.style_class);
|
||||
|
||||
@ -401,9 +404,17 @@ const PopupSeparatorMenuItem = new Lang.Class({
|
||||
this.parent({ reactive: false,
|
||||
can_focus: false});
|
||||
|
||||
this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
|
||||
this.addActor(this._drawingArea, { span: -1, expand: true });
|
||||
this._drawingArea.connect('repaint', Lang.bind(this, this._onRepaint));
|
||||
this._separator = new HorzSeparator({ style_class: 'popup-separator-menu-item' });
|
||||
this.addActor(this._separator.actor, { span: -1, expand: true });
|
||||
}
|
||||
});
|
||||
|
||||
const HorzSeparator = new Lang.Class({
|
||||
Name: 'HorzSeparator',
|
||||
|
||||
_init: function (params) {
|
||||
this.actor = new St.DrawingArea(params);
|
||||
this.actor.connect('repaint', Lang.bind(this, this._onRepaint));
|
||||
},
|
||||
|
||||
_onRepaint: function(area) {
|
||||
@ -763,7 +774,7 @@ const PopupSwitchMenuItem = new Lang.Class({
|
||||
{ expand: true, span: -1, align: St.Align.END });
|
||||
|
||||
this._statusLabel = new St.Label({ text: '',
|
||||
style_class: 'popup-inactive-menu-item'
|
||||
style_class: 'popup-status-menu-item'
|
||||
});
|
||||
this._statusBin.child = this._switch.actor;
|
||||
},
|
||||
@ -870,6 +881,12 @@ const PopupMenuBase = new Lang.Class({
|
||||
this._activeMenuItem = null;
|
||||
this._childMenus = [];
|
||||
this._settingsActions = { };
|
||||
|
||||
this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
this._setSettingsVisibility(Main.sessionMode.allowSettings);
|
||||
},
|
||||
|
||||
addAction: function(title, callback) {
|
||||
@ -883,9 +900,6 @@ const PopupMenuBase = new Lang.Class({
|
||||
},
|
||||
|
||||
addSettingsAction: function(title, desktopFile) {
|
||||
if (!Main.sessionMode.allowSettings)
|
||||
return null;
|
||||
|
||||
let menuItem = this.addAction(title, function() {
|
||||
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
|
||||
|
||||
@ -898,12 +912,13 @@ const PopupMenuBase = new Lang.Class({
|
||||
app.activate();
|
||||
});
|
||||
|
||||
menuItem.actor.visible = Main.sessionMode.allowSettings;
|
||||
this._settingsActions[desktopFile] = menuItem;
|
||||
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
setSettingsVisibility: function(visible) {
|
||||
_setSettingsVisibility: function(visible) {
|
||||
for (let id in this._settingsActions) {
|
||||
let item = this._settingsActions[id];
|
||||
item.actor.visible = visible;
|
||||
@ -1097,7 +1112,8 @@ const PopupMenuBase = new Lang.Class({
|
||||
let columnWidths = [];
|
||||
let items = this.box.get_children();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (!items[i].visible)
|
||||
if (!items[i].visible &&
|
||||
!(items[i]._delegate instanceof PopupSubMenu && items[i-1].visible))
|
||||
continue;
|
||||
if (items[i]._delegate instanceof PopupBaseMenuItem || items[i]._delegate instanceof PopupMenuBase) {
|
||||
let itemColumnWidths = items[i]._delegate.getColumnWidths();
|
||||
@ -1172,6 +1188,9 @@ const PopupMenuBase = new Lang.Class({
|
||||
this.actor.destroy();
|
||||
|
||||
this.emit('destroy');
|
||||
|
||||
Main.sessionMode.disconnect(this._sessionUpdatedId);
|
||||
this._sessionUpdatedId = 0;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(PopupMenuBase.prototype);
|
||||
@ -1258,13 +1277,14 @@ const PopupMenu = new Lang.Class({
|
||||
},
|
||||
|
||||
close: function(animate) {
|
||||
if (!this.isOpen)
|
||||
return;
|
||||
|
||||
if (this._activeMenuItem)
|
||||
this._activeMenuItem.setActive(false);
|
||||
|
||||
this._boxPointer.hide(animate);
|
||||
if (this._boxPointer.actor.visible)
|
||||
this._boxPointer.hide(animate);
|
||||
|
||||
if (!this.isOpen)
|
||||
return;
|
||||
|
||||
this.isOpen = false;
|
||||
this.emit('open-state-changed', false);
|
||||
@ -1337,6 +1357,11 @@ const PopupSubMenu = new Lang.Class({
|
||||
this.actor.vscrollbar_policy =
|
||||
needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER;
|
||||
|
||||
if (needsScrollbar)
|
||||
this.actor.add_style_pseudo_class('scrolled');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('scrolled');
|
||||
|
||||
// It looks funny if we animate with a scrollbar (at what point is
|
||||
// the scrollbar added?) so just skip that case
|
||||
if (animate && needsScrollbar)
|
||||
@ -2041,6 +2066,9 @@ const PopupMenuManager = new Lang.Class({
|
||||
},
|
||||
|
||||
addMenu: function(menu, position) {
|
||||
if (this._findMenu(menu) > -1)
|
||||
return;
|
||||
|
||||
let menudata = {
|
||||
menu: menu,
|
||||
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
|
||||
|
@ -27,6 +27,9 @@ const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
|
||||
<method name="ActivateResult">
|
||||
<arg type="s" direction="in" />
|
||||
</method>
|
||||
<method name="LaunchSearch">
|
||||
<arg type="as" direction="in" />
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
|
||||
@ -112,6 +115,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
|
||||
this.parent(title.toUpperCase());
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
this.icon = icon;
|
||||
},
|
||||
|
||||
createIcon: function(size, meta) {
|
||||
@ -126,9 +130,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
width, height, rowStride, size);
|
||||
}
|
||||
|
||||
// Ugh, but we want to fall back to something ...
|
||||
return new St.Icon({ icon_name: 'text-x-generic',
|
||||
icon_size: size });
|
||||
return null;
|
||||
},
|
||||
|
||||
_getResultsFinished: function(results, error) {
|
||||
@ -196,6 +198,10 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
|
||||
activateResult: function(id) {
|
||||
this._proxy.ActivateResultRemote(id);
|
||||
},
|
||||
|
||||
launchSearch: function(terms) {
|
||||
this._proxy.LaunchSearchRemote(terms);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -202,11 +202,12 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
|
||||
let label = new St.Label({ style_class: 'run-dialog-label',
|
||||
text: _("Please enter a command:") });
|
||||
text: _("Enter a Command") });
|
||||
|
||||
this.contentLayout.add(label, { y_align: St.Align.START });
|
||||
|
||||
let entry = new St.Entry({ style_class: 'run-dialog-entry' });
|
||||
let entry = new St.Entry({ style_class: 'run-dialog-entry',
|
||||
can_focus: true });
|
||||
ShellEntry.addContextMenu(entry);
|
||||
|
||||
entry.label_actor = label;
|
||||
@ -219,7 +220,9 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
this.contentLayout.add(this._errorBox, { expand: true });
|
||||
|
||||
let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
|
||||
let errorIcon = new St.Icon({ icon_name: 'dialog-error-symbolic',
|
||||
icon_size: 24,
|
||||
style_class: 'run-dialog-error-icon' });
|
||||
|
||||
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
|
||||
|
||||
@ -234,6 +237,10 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
this._errorBox.hide();
|
||||
|
||||
this.setButtons([{ action: Lang.bind(this, this.close),
|
||||
label: _("Close"),
|
||||
key: Clutter.Escape }]);
|
||||
|
||||
this._pathCompleter = new Gio.FilenameCompleter();
|
||||
this._commandCompleter = new CommandCompleter();
|
||||
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
||||
@ -244,20 +251,12 @@ const RunDialog = new Lang.Class({
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
this.popModal();
|
||||
if (e.get_state() & Clutter.ModifierType.CONTROL_MASK)
|
||||
this._run(o.get_text(), true);
|
||||
else
|
||||
this._run(o.get_text(), false);
|
||||
if (!this._commandError)
|
||||
this._run(o.get_text(),
|
||||
e.get_state() & Clutter.ModifierType.CONTROL_MASK);
|
||||
if (!this._commandError ||
|
||||
!this.pushModal())
|
||||
this.close();
|
||||
else {
|
||||
if (!this.pushModal())
|
||||
this.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.slash) {
|
||||
|
@ -17,16 +17,19 @@ const Layout = imports.ui.layout;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||
|
||||
const CURTAIN_SLIDE_TIME = 0.5;
|
||||
const CURTAIN_SLIDE_TIME = 0.3;
|
||||
// fraction of screen height the arrow must reach before completing
|
||||
// the slide up automatically
|
||||
const ARROW_DRAG_TRESHOLD = 0.1;
|
||||
const ARROW_DRAG_THRESHOLD = 0.1;
|
||||
|
||||
// Parameters for the arrow animation
|
||||
const N_ARROWS = 3;
|
||||
@ -44,7 +47,7 @@ const SUMMARY_ICON_SIZE = 48;
|
||||
// STANDARD_FADE_TIME is used when the session goes idle, while
|
||||
// SHORT_FADE_TIME is used when requesting lock explicitly from the user menu
|
||||
const STANDARD_FADE_TIME = 10;
|
||||
const SHORT_FADE_TIME = 0.8;
|
||||
const SHORT_FADE_TIME = 0.3;
|
||||
|
||||
const Clock = new Lang.Class({
|
||||
Name: 'ScreenShieldClock',
|
||||
@ -93,13 +96,13 @@ const NotificationsBox = new Lang.Class({
|
||||
|
||||
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.MIDDLE });
|
||||
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
|
||||
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'screen-shield-notifications-box' });
|
||||
scrollView.add_actor(this._persistentNotificationBox);
|
||||
|
||||
this.actor.add(this._residentNotificationBox, { x_fill: true });
|
||||
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.MIDDLE });
|
||||
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
|
||||
|
||||
this._items = [];
|
||||
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
|
||||
@ -124,13 +127,12 @@ const NotificationsBox = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
if (this._residentNotificationBox.get_n_children() > 0) {
|
||||
this.actor.show();
|
||||
return;
|
||||
}
|
||||
this._residentNotificationBox.visible = this._residentNotificationBox.get_n_children() > 0;
|
||||
this._persistentNotificationBox.visible = this._persistentNotificationBox.get_children().some(function(a) {
|
||||
return a.visible;
|
||||
});
|
||||
|
||||
let children = this._persistentNotificationBox.get_children();
|
||||
this.actor.visible = children.some(function(a) { return a.visible; });
|
||||
this.actor.visible = this._residentNotificationBox.visible || this._persistentNotificationBox.visible;
|
||||
},
|
||||
|
||||
_sourceIsResident: function(source) {
|
||||
@ -151,7 +153,7 @@ const NotificationsBox = new Lang.Class({
|
||||
box.add(sourceActor.actor, { y_fill: true });
|
||||
|
||||
let textBox = new St.BoxLayout({ vertical: true });
|
||||
box.add(textBox);
|
||||
box.add(textBox, { y_fill: false, y_align: St.Align.START });
|
||||
|
||||
let label = new St.Label({ text: source.title,
|
||||
style_class: 'screen-shield-notification-label' });
|
||||
@ -184,11 +186,11 @@ const NotificationsBox = new Lang.Class({
|
||||
|
||||
if (obj.resident) {
|
||||
this._residentNotificationBox.add(item.notificationStackWidget);
|
||||
item.closeButtonVisible = false;
|
||||
item.closeButton.hide();
|
||||
item.prepareNotificationStackForShowing();
|
||||
} else {
|
||||
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
|
||||
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.MIDDLE });
|
||||
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
|
||||
}
|
||||
|
||||
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
|
||||
@ -234,7 +236,7 @@ const NotificationsBox = new Lang.Class({
|
||||
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
|
||||
|
||||
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
|
||||
this._persistentNotificationBox.add(obj.sourceBox);
|
||||
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
|
||||
} else if (itemShouldBeResident && !obj.resident) {
|
||||
// make into a resident item
|
||||
obj.sourceBox.destroy();
|
||||
@ -242,7 +244,7 @@ const NotificationsBox = new Lang.Class({
|
||||
obj.resident = true;
|
||||
|
||||
this._residentNotificationBox.add(obj.item.notificationStackWidget);
|
||||
obj.item.closeButtonVisible = false;
|
||||
obj.item.closeButton.hide();
|
||||
obj.item.prepareNotificationStackForShowing();
|
||||
} else {
|
||||
// just update the counter
|
||||
@ -365,7 +367,8 @@ const ScreenShield = new Lang.Class({
|
||||
name: 'lockScreenContents' });
|
||||
this._lockScreenContents.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
this._background = new St.Bin({ style_class: 'screen-shield-background',
|
||||
child: Meta.BackgroundActor.new_for_screen(global.screen) });
|
||||
this._lockScreenGroup.add_actor(this._background);
|
||||
this._lockScreenGroup.add_actor(this._lockScreenContents);
|
||||
|
||||
@ -395,6 +398,7 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._lockDialogGroup = new St.Widget({ x_expand: true,
|
||||
y_expand: true,
|
||||
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
|
||||
name: 'lockDialogGroup' });
|
||||
|
||||
this.actor.add_actor(this._lockDialogGroup);
|
||||
@ -412,6 +416,8 @@ const ScreenShield = new Lang.Class({
|
||||
this._onStatusChanged(status);
|
||||
}));
|
||||
|
||||
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._loginSession = this._loginManager.getCurrentSessionProxy();
|
||||
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
|
||||
@ -420,8 +426,11 @@ const ScreenShield = new Lang.Class({
|
||||
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
|
||||
this._isModal = false;
|
||||
this._isLocked = false;
|
||||
this._hasLockScreen = false;
|
||||
this._isGreeter = false;
|
||||
this._isActive = false;
|
||||
this._inUnlockAnimation = false;
|
||||
this._activationTime = 0;
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||
{ inhibitEvents: true,
|
||||
@ -430,17 +439,28 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_onLockScreenKeyRelease: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this._ensureUnlockDialog();
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
// Do nothing if the lock screen is not fully shown.
|
||||
// This avoids reusing the previous (and stale) unlock
|
||||
// dialog if esc is pressed while the curtain is going
|
||||
// down after cancel.
|
||||
// Similarly, don't bump if the lock screen is not showing or is
|
||||
// animating, as the bump overrides the animation and would
|
||||
// remove any onComplete handler.
|
||||
|
||||
if (this._lockScreenState != MessageTray.State.SHOWN)
|
||||
return false;
|
||||
|
||||
if (symbol == Clutter.KEY_Escape ||
|
||||
symbol == Clutter.KEY_Return ||
|
||||
symbol == Clutter.KEY_KP_Enter) {
|
||||
this._ensureUnlockDialog(true);
|
||||
this._hideLockScreen(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't bump if the lock screen is not showing or is
|
||||
// animating, as the bump overrides the animation and would
|
||||
// remove any onComplete handler
|
||||
if (this._lockScreenState == MessageTray.State.SHOWN)
|
||||
this._bumpLockScreen();
|
||||
this._bumpLockScreen();
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -458,8 +478,8 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
// 7 standard scrolls to lift up
|
||||
if (this._lockScreenScrollCounter > 35) {
|
||||
this._ensureUnlockDialog();
|
||||
this._hideLockScreen(0);
|
||||
this._ensureUnlockDialog(false);
|
||||
this._hideLockScreen(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -490,11 +510,11 @@ const ScreenShield = new Lang.Class({
|
||||
_onDragBegin: function() {
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
this._lockScreenState = MessageTray.State.HIDING;
|
||||
this._ensureUnlockDialog();
|
||||
this._ensureUnlockDialog(false);
|
||||
},
|
||||
|
||||
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
|
||||
if (this._lockScreenGroup.y < -(ARROW_DRAG_TRESHOLD * global.stage.height)) {
|
||||
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
|
||||
// Complete motion automatically
|
||||
this._hideLockScreen(true);
|
||||
} else {
|
||||
@ -506,7 +526,7 @@ const ScreenShield = new Lang.Class({
|
||||
Tweener.addTween(this._lockScreenGroup,
|
||||
{ y: 0,
|
||||
time: time,
|
||||
transition: 'linear',
|
||||
transition: 'easeInQuad',
|
||||
onComplete: function() {
|
||||
this._lockScreenGroup.fixed_position_set = false;
|
||||
this._lockScreenState = MessageTray.State.SHOWN;
|
||||
@ -517,7 +537,7 @@ const ScreenShield = new Lang.Class({
|
||||
// If we have a unlock dialog, cancel it
|
||||
if (this._dialog) {
|
||||
this._dialog.cancel();
|
||||
if (!this._keepDialog) {
|
||||
if (!this._isGreeter) {
|
||||
this._dialog = null;
|
||||
}
|
||||
}
|
||||
@ -528,7 +548,7 @@ const ScreenShield = new Lang.Class({
|
||||
if (status == GnomeSession.PresenceStatus.IDLE) {
|
||||
if (this._dialog) {
|
||||
this._dialog.cancel();
|
||||
if (!this._keepDialog) {
|
||||
if (!this._isGreeter) {
|
||||
this._dialog = null;
|
||||
}
|
||||
}
|
||||
@ -538,14 +558,18 @@ const ScreenShield = new Lang.Class({
|
||||
this._isModal = true;
|
||||
}
|
||||
|
||||
if (!this._isLocked)
|
||||
if (!this._isActive) {
|
||||
this._lightbox.show();
|
||||
|
||||
if (this._activationTime == 0)
|
||||
this._activationTime = GLib.get_monotonic_time();
|
||||
}
|
||||
} else {
|
||||
let lightboxWasShown = this._lightbox.shown;
|
||||
this._lightbox.hide();
|
||||
|
||||
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
|
||||
if (shouldLock || this._isLocked) {
|
||||
if (shouldLock || this._isActive) {
|
||||
this.lock(false);
|
||||
} else if (this._isModal) {
|
||||
this.unlock();
|
||||
@ -554,8 +578,20 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
showDialog: function() {
|
||||
this.lock(true);
|
||||
this._ensureUnlockDialog();
|
||||
// Ensure that the stage window is mapped, before taking a grab
|
||||
// otherwise X errors out
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
if (!this._isModal) {
|
||||
Main.pushModal(this.actor);
|
||||
this._isModal = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}));
|
||||
|
||||
this.actor.show();
|
||||
this._isGreeter = Main.sessionMode.isGreeter;
|
||||
this._ensureUnlockDialog(true);
|
||||
this._hideLockScreen(false);
|
||||
},
|
||||
|
||||
@ -586,33 +622,35 @@ const ScreenShield = new Lang.Class({
|
||||
Tweener.addTween(this._lockScreenGroup,
|
||||
{ y: -h,
|
||||
time: time,
|
||||
transition: 'linear',
|
||||
transition: 'easeInQuad',
|
||||
onComplete: function() {
|
||||
this._lockScreenHidden();
|
||||
this._lockScreenState = MessageTray.State.HIDDEN;
|
||||
this._lockScreenGroup.hide();
|
||||
},
|
||||
onCompleteScope: this,
|
||||
});
|
||||
} else {
|
||||
this._lockScreenHidden();
|
||||
this._lockScreenState = MessageTray.State.HIDDEN;
|
||||
this._lockScreenGroup.hide();
|
||||
}
|
||||
|
||||
if (Main.sessionMode.currentMode == 'lock-screen')
|
||||
Main.sessionMode.popMode('lock-screen');
|
||||
},
|
||||
|
||||
_lockScreenHidden: function() {
|
||||
this._lockScreenState = MessageTray.State.HIDDEN;
|
||||
this._lockScreenGroup.hide();
|
||||
},
|
||||
|
||||
_ensureUnlockDialog: function() {
|
||||
_ensureUnlockDialog: function(onPrimary) {
|
||||
if (!this._dialog) {
|
||||
[this._dialog, this._keepDialog] = Main.sessionMode.createUnlockDialog(this._lockDialogGroup);
|
||||
let constructor = Main.sessionMode.unlockDialog;
|
||||
this._dialog = new constructor(this._lockDialogGroup);
|
||||
if (!this._dialog) {
|
||||
// This session mode has no locking capabilities
|
||||
this.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
let time = global.get_current_time();
|
||||
this._dialog.connect('loaded', Lang.bind(this, function() {
|
||||
if (!this._dialog.open()) {
|
||||
if (!this._dialog.open(time, onPrimary)) {
|
||||
log('Could not open login dialog: failed to acquire grab');
|
||||
this.unlock();
|
||||
}
|
||||
@ -621,45 +659,42 @@ const ScreenShield = new Lang.Class({
|
||||
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
|
||||
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
|
||||
}
|
||||
|
||||
if (this._keepDialog) {
|
||||
// Notify the other components that even though we are showing the
|
||||
// screenshield, we're not in a locked state
|
||||
// (this happens for the gdm greeter)
|
||||
|
||||
this._isLocked = false;
|
||||
this.emit('lock-status-changed', false);
|
||||
}
|
||||
},
|
||||
|
||||
_onUnlockFailed: function() {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
|
||||
this._resetLockScreen(false);
|
||||
this._resetLockScreen(true, false);
|
||||
},
|
||||
|
||||
_onUnlockSucceded: function() {
|
||||
this.unlock();
|
||||
this._tweenUnlocked();
|
||||
},
|
||||
|
||||
_resetLockScreen: function(animate) {
|
||||
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
|
||||
this._ensureLockScreen();
|
||||
this._lockDialogGroup.scale_x = 1;
|
||||
this._lockDialogGroup.scale_y = 1;
|
||||
|
||||
this._lockScreenGroup.show();
|
||||
this._lockScreenState = MessageTray.State.SHOWING;
|
||||
|
||||
if (animate) {
|
||||
if (animateLockScreen) {
|
||||
this._lockScreenGroup.y = -global.screen_height;
|
||||
Tweener.removeTweens(this._lockScreenGroup);
|
||||
Tweener.addTween(this._lockScreenGroup,
|
||||
{ y: 0,
|
||||
time: SHORT_FADE_TIME,
|
||||
transition: 'linear',
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._lockScreenShown();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
} else {
|
||||
this._lockScreenGroup.fixed_position_set = false;
|
||||
this._lockScreenShown();
|
||||
}
|
||||
|
||||
if (animateLockDialog) {
|
||||
this._lockDialogGroup.opacity = 0;
|
||||
Tweener.removeTweens(this._lockDialogGroup);
|
||||
Tweener.addTween(this._lockDialogGroup,
|
||||
@ -668,13 +703,20 @@ const ScreenShield = new Lang.Class({
|
||||
transition: 'easeOutQuad' });
|
||||
} else {
|
||||
this._lockDialogGroup.opacity = 255;
|
||||
this._lockScreenShown();
|
||||
}
|
||||
|
||||
this._lockScreenGroup.grab_key_focus();
|
||||
|
||||
if (Main.sessionMode.currentMode != 'lock-screen')
|
||||
Main.sessionMode.pushMode('lock-screen');
|
||||
},
|
||||
|
||||
_lockScreenShown: function() {
|
||||
if (this._dialog && !this._isGreeter) {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
}
|
||||
|
||||
if (this._arrowAnimationId)
|
||||
Mainloop.source_remove(this._arrowAnimationId);
|
||||
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
|
||||
@ -689,12 +731,16 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
// Some of the actors in the lock screen are heavy in
|
||||
// resources, so we only create them when needed
|
||||
_prepareLockScreen: function() {
|
||||
_ensureLockScreen: function() {
|
||||
if (this._hasLockScreen)
|
||||
return;
|
||||
|
||||
this._lockScreenContentsBox = new St.BoxLayout({ x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
vertical: true });
|
||||
vertical: true,
|
||||
style_class: 'screen-shield-contents-box' });
|
||||
this._clock = new Clock();
|
||||
this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
|
||||
y_fill: true });
|
||||
@ -731,22 +777,38 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
get locked() {
|
||||
return this._isLocked;
|
||||
return this._isActive;
|
||||
},
|
||||
|
||||
get activationTime() {
|
||||
return this._activationTime;
|
||||
},
|
||||
|
||||
_tweenUnlocked: function() {
|
||||
this._inUnlockAnimation = true;
|
||||
this.unlock();
|
||||
Tweener.addTween(this._lockDialogGroup, {
|
||||
scale_x: 0,
|
||||
scale_y: 0,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
if (this._dialog) {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
}
|
||||
this.actor.hide();
|
||||
this._inUnlockAnimation = false;
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
unlock: function() {
|
||||
if (this._hasLockScreen)
|
||||
this._clearLockScreen();
|
||||
|
||||
if (this._keepDialog) {
|
||||
// The dialog must be kept alive,
|
||||
// so immediately go back to it
|
||||
// This will also reset _isLocked
|
||||
this._ensureUnlockDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._dialog) {
|
||||
if (this._dialog && !this._isGreeter) {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
}
|
||||
@ -758,26 +820,100 @@ const ScreenShield = new Lang.Class({
|
||||
this._isModal = false;
|
||||
}
|
||||
|
||||
this._isLocked = false;
|
||||
this.actor.hide();
|
||||
if (!this._inUnlockAnimation)
|
||||
this.actor.hide();
|
||||
|
||||
this.emit('lock-status-changed', false);
|
||||
if (Main.sessionMode.currentMode == 'lock-screen')
|
||||
Main.sessionMode.popMode('lock-screen');
|
||||
if (Main.sessionMode.currentMode == 'unlock-dialog')
|
||||
Main.sessionMode.popMode('unlock-dialog');
|
||||
|
||||
this._activationTime = 0;
|
||||
this._isActive = false;
|
||||
this.emit('lock-status-changed');
|
||||
},
|
||||
|
||||
lock: function(animate) {
|
||||
if (!this._hasLockScreen)
|
||||
this._prepareLockScreen();
|
||||
|
||||
if (!this._isModal) {
|
||||
Main.pushModal(this.actor);
|
||||
this._isModal = true;
|
||||
}
|
||||
|
||||
this._isLocked = true;
|
||||
this.actor.show();
|
||||
this._resetLockScreen(animate);
|
||||
if (this._activationTime == 0)
|
||||
this._activationTime = GLib.get_monotonic_time();
|
||||
|
||||
this.emit('lock-status-changed', true);
|
||||
this.actor.show();
|
||||
|
||||
if (Main.sessionMode.currentMode != 'unlock-dialog' &&
|
||||
Main.sessionMode.currentMode != 'lock-screen') {
|
||||
this._isGreeter = Main.sessionMode.isGreeter;
|
||||
if (!this._isGreeter)
|
||||
Main.sessionMode.pushMode('unlock-dialog');
|
||||
}
|
||||
|
||||
this._resetLockScreen(animate, animate);
|
||||
|
||||
this._isActive = true;
|
||||
this.emit('lock-status-changed');
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(ScreenShield.prototype);
|
||||
|
||||
/* Fallback code to handle session locking using gnome-screensaver,
|
||||
in case the required GDM dependency is not there
|
||||
*/
|
||||
const ScreenShieldFallback = new Lang.Class({
|
||||
Name: 'ScreenShieldFallback',
|
||||
|
||||
_init: function() {
|
||||
Util.spawn(['gnome-screensaver']);
|
||||
|
||||
this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||
g_name: 'org.gnome.ScreenSaver',
|
||||
g_object_path: '/org/gnome/ScreenSaver',
|
||||
g_interface_name: 'org.gnome.ScreenSaver',
|
||||
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
|
||||
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES),
|
||||
});
|
||||
this._proxy.init(null);
|
||||
|
||||
this._proxy.connect('g-signal', Lang.bind(this, this._onSignal));
|
||||
this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged));
|
||||
},
|
||||
|
||||
_onNameOwnerChanged: function(object, pspec) {
|
||||
if (this._proxy.g_name_owner)
|
||||
[this._locked] = this._proxy.call_sync('GetActive', null,
|
||||
Gio.DBusCallFlags.NONE, -1, null).deep_unpack();
|
||||
else
|
||||
this._locked = false;
|
||||
|
||||
this.emit('lock-status-changed', this._locked);
|
||||
},
|
||||
|
||||
_onSignal: function(proxy, senderName, signalName, params) {
|
||||
if (signalName == 'ActiveChanged') {
|
||||
[this._locked] = params.deep_unpack();
|
||||
this.emit('lock-status-changed', this._locked);
|
||||
}
|
||||
},
|
||||
|
||||
get locked() {
|
||||
return this._locked;
|
||||
},
|
||||
|
||||
lock: function() {
|
||||
this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null,
|
||||
Lang.bind(this, function(proxy, result) {
|
||||
proxy.call_finish(result);
|
||||
|
||||
this.emit('lock-screen-shown');
|
||||
}));
|
||||
},
|
||||
|
||||
unlock: function() {
|
||||
this._proxy.call('SetActive', GLib.Variant.new('(b)', false),
|
||||
Gio.DBusCallFlags.NONE, -1, null, null);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(ScreenShieldFallback.prototype);
|
||||
|
@ -163,6 +163,16 @@ const SearchProvider = new Lang.Class({
|
||||
*/
|
||||
activateResult: function(id) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* launchSearch:
|
||||
* @terms: Current search terms
|
||||
*
|
||||
* Called when the user clicks the provider icon.
|
||||
*/
|
||||
launchSearch: function(terms) {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SearchProvider.prototype);
|
||||
@ -261,5 +271,11 @@ const SearchSystem = new Lang.Class({
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
launchSearch: function(provider) {
|
||||
let terms = this.getTerms();
|
||||
provider.launchSearch(terms);
|
||||
Main.overview.toggle();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SearchSystem.prototype);
|
||||
|
@ -4,25 +4,191 @@ const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const MAX_SEARCH_RESULTS_ROWS = 1;
|
||||
const MAX_SEARCH_RESULTS_ROWS = 3;
|
||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
|
||||
const SearchResult = new Lang.Class({
|
||||
Name: 'SearchResult',
|
||||
const ListSearchResult = new Lang.Class({
|
||||
Name: 'ListSearchResult',
|
||||
|
||||
ICON_SIZE: 64,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.actor = new St.Button({ style_class: 'search-result',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
let content = new St.BoxLayout({ style_class: 'search-result-content',
|
||||
vertical: false });
|
||||
this._content = content;
|
||||
this.actor.set_child(content);
|
||||
|
||||
// An icon for, or thumbnail of, content
|
||||
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
|
||||
if (icon) {
|
||||
let iconBin = new St.Bin({ style_class: 'search-result-icon',
|
||||
child: icon });
|
||||
content.add(iconBin);
|
||||
}
|
||||
|
||||
let details = new St.BoxLayout({ style_class: 'search-result-details',
|
||||
vertical: true });
|
||||
content.add(details, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let title = new St.Label({ style_class: 'search-result-details-title',
|
||||
text: this.metaInfo['name'] })
|
||||
details.add(title, { x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
// TODO: could highlight terms in the description here, or should
|
||||
// providers provide what should be highlighted?
|
||||
if (this.metaInfo['description']) {
|
||||
let description = new St.Label({ style_class: 'search-result-details-description',
|
||||
text: this.metaInfo['description'] });
|
||||
details.add(description, { x_fill: false,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.END });
|
||||
}
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this.actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
_onResultClicked: function(actor) {
|
||||
this.activate();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const ListSearchResults = new Lang.Class({
|
||||
Name: 'ListSearchResults',
|
||||
Extends: Search.SearchResultDisplay,
|
||||
|
||||
_init: function(provider) {
|
||||
this.parent(provider);
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'results-list',
|
||||
vertical: true });
|
||||
this.actor.connect('notify::width', Lang.bind(this, function() {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
let results = this.getResultsForDisplay();
|
||||
if (results.length == 0)
|
||||
return;
|
||||
|
||||
provider.getResultMetas(results, Lang.bind(this, this.renderResults));
|
||||
}));
|
||||
}));
|
||||
this._notDisplayedResult = [];
|
||||
this._terms = [];
|
||||
this._pendingClear = false;
|
||||
},
|
||||
|
||||
getResultsForDisplay: function() {
|
||||
let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
|
||||
let canDisplay = MAX_SEARCH_RESULTS_ROWS - alreadyVisible;
|
||||
|
||||
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
|
||||
|
||||
return this._notDisplayedResult.splice(0, numResults);
|
||||
},
|
||||
|
||||
getVisibleResultCount: function() {
|
||||
return this.actor.get_n_children();
|
||||
},
|
||||
|
||||
hasMoreResults: function() {
|
||||
return this._notDisplayedResult.length > 0;
|
||||
},
|
||||
|
||||
setResults: function(results, terms) {
|
||||
// copy the lists
|
||||
this._notDisplayedResult = results.slice(0);
|
||||
this._terms = terms.slice(0);
|
||||
this._pendingClear = true;
|
||||
},
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||
this.addItem(display.actor);
|
||||
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._onFocusedProviderChanged));
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this.actor.destroy_all_children();
|
||||
this._pendingClear = false;
|
||||
},
|
||||
|
||||
getFirstResult: function() {
|
||||
if (this.getVisibleResultCount() > 0)
|
||||
return this.getItemAtIndex(0)._delegate;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
addItem: function(actor, index) {
|
||||
if (index !== undefined)
|
||||
this.actor.insert_child_at_index(actor, index);
|
||||
else
|
||||
this.actor.add_actor(actor);
|
||||
},
|
||||
|
||||
getItemAtIndex: function(index) {
|
||||
return this.actor.get_child_at_index(index);
|
||||
},
|
||||
|
||||
_onFocusedProviderChanged: function() {
|
||||
this.emit('focused-provider-changed');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ListSearchResults.prototype);
|
||||
|
||||
|
||||
const GridSearchResult = new Lang.Class({
|
||||
Name: 'GridSearchResult',
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.actor = new St.Button({ style_class: 'grid-search-result',
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
@ -31,7 +197,7 @@ const SearchResult = new Lang.Class({
|
||||
|
||||
let content = provider.createResultActor(metaInfo, terms);
|
||||
if (content == null) {
|
||||
content = new St.Bin({ style_class: 'search-result-content',
|
||||
content = new St.Bin({ style_class: 'grid-search-result-content',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
track_hover: true,
|
||||
@ -45,7 +211,7 @@ const SearchResult = new Lang.Class({
|
||||
if (content._delegate && content._delegate.getDragActorSource)
|
||||
this._dragActorSource = content._delegate.getDragActorSource();
|
||||
}
|
||||
this._content = content;
|
||||
this.content = content;
|
||||
this.actor.set_child(content);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
|
||||
@ -53,23 +219,23 @@ const SearchResult = new Lang.Class({
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
Main.overview.beginAppDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-cancelled',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.cancelledItemDrag(this);
|
||||
Main.overview.cancelledAppDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
Main.overview.endAppDrag(this);
|
||||
}));
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this._content.add_style_pseudo_class('selected');
|
||||
this.content.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._content.remove_style_pseudo_class('selected');
|
||||
this.content.remove_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
@ -85,7 +251,7 @@ const SearchResult = new Lang.Class({
|
||||
if (this._dragActorSource)
|
||||
return this._dragActorSource;
|
||||
// not exactly right, but alignment problems are hard to notice
|
||||
return this._content;
|
||||
return this.content;
|
||||
},
|
||||
|
||||
getDragActor: function(stageX, stageY) {
|
||||
@ -108,11 +274,11 @@ const GridSearchResults = new Lang.Class({
|
||||
_init: function(provider, grid) {
|
||||
this.parent(provider);
|
||||
|
||||
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this.actor = new St.Bin({ x_align: St.Align.START });
|
||||
|
||||
this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||
this.actor.set_child(this._grid.actor);
|
||||
|
||||
this._width = 0;
|
||||
this.actor.connect('notify::width', Lang.bind(this, function() {
|
||||
this._width = this.actor.width;
|
||||
@ -130,6 +296,7 @@ const GridSearchResults = new Lang.Class({
|
||||
},
|
||||
|
||||
getResultsForDisplay: function() {
|
||||
this._grid.actor.ensure_style();
|
||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
||||
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
|
||||
- alreadyVisible;
|
||||
@ -143,6 +310,10 @@ const GridSearchResults = new Lang.Class({
|
||||
return this._grid.visibleItemsCount();
|
||||
},
|
||||
|
||||
hasMoreResults: function() {
|
||||
return this._notDisplayedResult.length > 0;
|
||||
},
|
||||
|
||||
setResults: function(results, terms) {
|
||||
// copy the lists
|
||||
this._notDisplayedResult = results.slice(0);
|
||||
@ -152,8 +323,10 @@ const GridSearchResults = new Lang.Class({
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new SearchResult(this.provider, metas[i], this._terms);
|
||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
||||
this._grid.addItem(display.actor);
|
||||
|
||||
display.content.connect('key-focus-in', Lang.bind(this, this._onFocusedProviderChanged));
|
||||
}
|
||||
},
|
||||
|
||||
@ -167,11 +340,17 @@ const GridSearchResults = new Lang.Class({
|
||||
return this._grid.getItemAtIndex(0)._delegate;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
_onFocusedProviderChanged: function() {
|
||||
this.emit('focused-provider-changed');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(GridSearchResults.prototype);
|
||||
|
||||
const SearchResults = new Lang.Class({
|
||||
Name: 'SearchResults',
|
||||
|
||||
const SearchDisplay = new Lang.Class({
|
||||
Name: 'SearchDisplay',
|
||||
|
||||
_init: function(searchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
@ -214,8 +393,6 @@ const SearchResults = new Lang.Class({
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
this.createProviderMeta(this._providers[i]);
|
||||
}
|
||||
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
|
||||
this.actor.add(this._searchProvidersBox);
|
||||
|
||||
this._highlightDefault = false;
|
||||
this._defaultResult = null;
|
||||
@ -224,19 +401,43 @@ const SearchResults = new Lang.Class({
|
||||
createProviderMeta: function(provider) {
|
||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
let title = new St.Label({ style_class: 'search-section-header',
|
||||
text: provider.title });
|
||||
providerBox.add(title);
|
||||
let providerBoxContent = new St.BoxLayout({ style_class: 'search-section-content' });
|
||||
let isAppsProvider = (provider instanceof AppDisplay.AppSearchProvider);
|
||||
|
||||
let providerIcon;
|
||||
if (!isAppsProvider) {
|
||||
let separator = new PopupMenu.HorzSeparator({ style_class: 'search-section-separator' });
|
||||
providerBox.add(separator.actor, { expand: true });
|
||||
|
||||
providerIcon = new ProviderIcon(provider);
|
||||
providerIcon.connect('launch-search', Lang.bind(this, function(providerIcon) {
|
||||
this._searchSystem.launchSearch(providerIcon.provider);
|
||||
}));
|
||||
providerBoxContent.add(providerIcon.actor, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
}
|
||||
|
||||
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
providerBox.add(resultDisplayBin, { expand: true });
|
||||
let resultDisplay = new GridSearchResults(provider);
|
||||
providerBoxContent.add(resultDisplayBin, { expand: true });
|
||||
|
||||
providerBox.add(providerBoxContent);
|
||||
|
||||
let resultDisplay;
|
||||
if (isAppsProvider)
|
||||
resultDisplay = new GridSearchResults(provider);
|
||||
else
|
||||
resultDisplay = new ListSearchResults(provider);
|
||||
|
||||
resultDisplay.connect('focused-provider-changed', Lang.bind(this, this._updateProviderIconCanFocus));
|
||||
resultDisplayBin.set_child(resultDisplay.actor);
|
||||
|
||||
this._providerMeta.push({ provider: provider,
|
||||
actor: providerBox,
|
||||
icon: providerIcon,
|
||||
resultDisplay: resultDisplay });
|
||||
this._content.add(providerBox);
|
||||
},
|
||||
@ -257,6 +458,7 @@ const SearchResults = new Lang.Class({
|
||||
let meta = this._providerMeta[i];
|
||||
meta.resultDisplay.clear();
|
||||
meta.actor.hide();
|
||||
if (meta.icon) meta.icon.actor.can_focus = false;
|
||||
}
|
||||
},
|
||||
|
||||
@ -264,6 +466,7 @@ const SearchResults = new Lang.Class({
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.resultDisplay.clear();
|
||||
meta.actor.hide();
|
||||
if (meta.icon) meta.icon.actor.can_focus = false;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
@ -286,6 +489,11 @@ const SearchResults = new Lang.Class({
|
||||
return this._providerMeta[this._providers.indexOf(provider)];
|
||||
},
|
||||
|
||||
_metaForProvidersExcept: function(provider) {
|
||||
let skip = this._providers.indexOf(provider);
|
||||
return this._providerMeta.filter(function(meta, index) { return index != skip }, this);
|
||||
},
|
||||
|
||||
_maybeSetInitialSelection: function() {
|
||||
let newDefaultResult = null;
|
||||
|
||||
@ -302,9 +510,6 @@ const SearchResults = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (!newDefaultResult)
|
||||
newDefaultResult = this._searchProvidersBox.get_first_child();
|
||||
|
||||
if (newDefaultResult != this._defaultResult) {
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.setSelected(false);
|
||||
@ -346,6 +551,9 @@ const SearchResults = new Lang.Class({
|
||||
meta.resultDisplay.setResults(providerResults, terms);
|
||||
let results = meta.resultDisplay.getResultsForDisplay();
|
||||
|
||||
if (meta.icon)
|
||||
meta.icon.moreIcon.visible = meta.resultDisplay.hasMoreResults();
|
||||
|
||||
provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||
this._clearDisplayForProvider(provider);
|
||||
meta.actor.show();
|
||||
@ -379,6 +587,15 @@ const SearchResults = new Lang.Class({
|
||||
this._defaultResult.setSelected(highlight);
|
||||
},
|
||||
|
||||
_updateProviderIconCanFocus: function(resultDisplay) {
|
||||
let meta = this._metaForProvider(resultDisplay.provider);
|
||||
if (meta.icon)
|
||||
meta.icon.actor.can_focus = true;
|
||||
|
||||
let others = this._metaForProvidersExcept(resultDisplay.provider);
|
||||
others.forEach(function(meta) { if (meta.icon) meta.icon.actor.can_focus = false; });
|
||||
},
|
||||
|
||||
navigateFocus: function(direction) {
|
||||
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
|
||||
@ -398,3 +615,65 @@ const SearchResults = new Lang.Class({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ProviderIcon = new Lang.Class({
|
||||
Name: 'ProviderIcon',
|
||||
|
||||
PROVIDER_ICON_SIZE: 48,
|
||||
|
||||
MORE_ICON_SIZE: 16,
|
||||
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
|
||||
this.actor = new St.Button({ style_class: 'search-section-icon-bin',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onIconClicked));
|
||||
|
||||
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
this.actor.set_child(this._content);
|
||||
|
||||
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
this.moreIcon = new St.Icon({ style_class: 'search-section-icon-more',
|
||||
icon_size: this.MORE_ICON_SIZE,
|
||||
icon_name: 'list-add-symbolic',
|
||||
visible: false,
|
||||
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
// HACK: without these, ClutterBinLayout
|
||||
// ignores alignment properties on the actor
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
|
||||
this._iconBin = new St.Bin({ style_class: 'search-section-icon',
|
||||
width: this.PROVIDER_ICON_SIZE,
|
||||
height: this.PROVIDER_ICON_SIZE });
|
||||
|
||||
this._content.add_actor(this._iconBin);
|
||||
this._content.add_actor(this.moreIcon);
|
||||
|
||||
this._createProviderIcon();
|
||||
},
|
||||
|
||||
_createProviderIcon: function() {
|
||||
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE });
|
||||
|
||||
if (this.provider.icon)
|
||||
icon.gicon = this.provider.icon;
|
||||
else
|
||||
icon.icon_name = 'application-x-executable';
|
||||
|
||||
this._iconBin.set_child(icon);
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.emit('launch-search');
|
||||
},
|
||||
|
||||
_onIconClicked: function(actor) {
|
||||
this.activate();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ProviderIcon.prototype);
|
||||
|
@ -1,94 +1,160 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const DEFAULT_MODE = 'user';
|
||||
const DEFAULT_MODE = 'restrictive';
|
||||
|
||||
const _modes = {
|
||||
'gdm': { hasOverview: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
allowKeybindingsWhenModal: true,
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
createSession: Main.createGDMSession,
|
||||
createUnlockDialog: Main.createGDMLoginDialog,
|
||||
panel: {
|
||||
left: [],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'display', 'keyboard',
|
||||
'volume', 'battery', 'lockScreen', 'powerMenu']
|
||||
}
|
||||
},
|
||||
'restrictive': {
|
||||
hasOverview: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
allowKeybindingsWhenModal: false,
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
hasWindows: false,
|
||||
hasNotifications: false,
|
||||
isLocked: false,
|
||||
isGreeter: false,
|
||||
isPrimary: false,
|
||||
unlockDialog: null,
|
||||
components: [],
|
||||
panel: {
|
||||
left: [],
|
||||
center: [],
|
||||
right: []
|
||||
},
|
||||
},
|
||||
|
||||
'initial-setup': { hasOverview: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
allowExtensions: false,
|
||||
allowKeybindingsWhenModal: false,
|
||||
hasRunDialog: false,
|
||||
hasWorkspaces: false,
|
||||
createSession: Main.createInitialSetupSession,
|
||||
panel: {
|
||||
left: [],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'volume', 'lockScreen']
|
||||
}
|
||||
},
|
||||
'gdm': {
|
||||
allowKeybindingsWhenModal: true,
|
||||
hasNotifications: true,
|
||||
isGreeter: true,
|
||||
isPrimary: true,
|
||||
unlockDialog: imports.gdm.loginDialog.LoginDialog,
|
||||
components: ['polkitAgent'],
|
||||
panel: {
|
||||
left: ['logo'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'display', 'keyboard',
|
||||
'volume', 'battery', 'powerMenu']
|
||||
}
|
||||
},
|
||||
|
||||
'user': { hasOverview: true,
|
||||
showCalendarEvents: true,
|
||||
allowSettings: true,
|
||||
allowExtensions: true,
|
||||
allowKeybindingsWhenModal: false,
|
||||
hasRunDialog: true,
|
||||
hasWorkspaces: true,
|
||||
createSession: Main.createUserSession,
|
||||
createUnlockDialog: Main.createSessionUnlockDialog,
|
||||
panel: {
|
||||
left: ['activities', 'appMenu'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
|
||||
'network', 'battery', 'lockScreen', 'userMenu']
|
||||
}
|
||||
}
|
||||
'lock-screen': {
|
||||
isLocked: true,
|
||||
isGreeter: undefined,
|
||||
unlockDialog: undefined,
|
||||
components: ['polkitAgent', 'telepathyClient'],
|
||||
panel: {
|
||||
left: ['userMenu'],
|
||||
center: [],
|
||||
right: ['lockScreen']
|
||||
},
|
||||
},
|
||||
|
||||
'unlock-dialog': {
|
||||
isLocked: true,
|
||||
unlockDialog: undefined,
|
||||
components: ['polkitAgent', 'telepathyClient'],
|
||||
panel: {
|
||||
left: ['userMenu'],
|
||||
center: [],
|
||||
right: ['a11y', 'keyboard', 'lockScreen']
|
||||
},
|
||||
},
|
||||
|
||||
'initial-setup': {
|
||||
isPrimary: true,
|
||||
components: ['keyring'],
|
||||
panel: {
|
||||
left: [],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'volume']
|
||||
}
|
||||
},
|
||||
|
||||
'user': {
|
||||
hasOverview: true,
|
||||
showCalendarEvents: true,
|
||||
allowSettings: true,
|
||||
allowExtensions: true,
|
||||
hasRunDialog: true,
|
||||
hasWorkspaces: true,
|
||||
hasWindows: true,
|
||||
hasNotifications: true,
|
||||
isLocked: false,
|
||||
isPrimary: true,
|
||||
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
|
||||
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
|
||||
'keyring', 'recorder', 'autorunManager', 'automountManager'],
|
||||
panel: {
|
||||
left: ['activities', 'appMenu'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
|
||||
'network', 'battery', 'userMenu']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function listModes() {
|
||||
let modes = Object.getOwnPropertyNames(_modes);
|
||||
for (let i = 0; i < modes.length; i++)
|
||||
print(modes[i]);
|
||||
if (_modes[modes[i]].isPrimary)
|
||||
print(modes[i]);
|
||||
}
|
||||
|
||||
const SessionMode = new Lang.Class({
|
||||
Name: 'SessionMode',
|
||||
|
||||
_init: function() {
|
||||
let params = _modes[global.session_mode];
|
||||
global.connect('notify::session-mode', Lang.bind(this, this._sync));
|
||||
let mode = _modes[global.session_mode].isPrimary ? global.session_mode
|
||||
: 'user';
|
||||
this._modeStack = [mode];
|
||||
this._sync();
|
||||
},
|
||||
|
||||
pushMode: function(mode) {
|
||||
this._modeStack.push(mode);
|
||||
this._sync();
|
||||
},
|
||||
|
||||
popMode: function(mode) {
|
||||
if (this.currentMode != mode || this._modeStack.length === 1)
|
||||
throw new Error("Invalid SessionMode.popMode");
|
||||
this._modeStack.pop();
|
||||
this._sync();
|
||||
},
|
||||
|
||||
switchMode: function(to) {
|
||||
if (this.currentMode == to)
|
||||
return;
|
||||
this._modeStack[this._modeStack.length - 1] = to;
|
||||
this._sync();
|
||||
},
|
||||
|
||||
get currentMode() {
|
||||
return this._modeStack[this._modeStack.length - 1];
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let params = _modes[this.currentMode];
|
||||
params = Params.parse(params, _modes[DEFAULT_MODE]);
|
||||
|
||||
this._createSession = params.createSession;
|
||||
delete params.createSession;
|
||||
this._createUnlockDialog = params.createUnlockDialog;
|
||||
delete params.createUnlockDialog;
|
||||
// A simplified version of Lang.copyProperties, handles
|
||||
// undefined as a special case for "no change / inherit from previous mode"
|
||||
for (let prop in params) {
|
||||
if (params[prop] !== undefined)
|
||||
this[prop] = params[prop];
|
||||
}
|
||||
|
||||
Lang.copyProperties(params, this);
|
||||
},
|
||||
|
||||
createSession: function() {
|
||||
if (this._createSession)
|
||||
this._createSession();
|
||||
},
|
||||
|
||||
createUnlockDialog: function() {
|
||||
if (this._createUnlockDialog)
|
||||
return this._createUnlockDialog.apply(this, arguments);
|
||||
else
|
||||
return null;
|
||||
},
|
||||
this.emit('updated');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SessionMode.prototype);
|
||||
|
@ -57,7 +57,10 @@ const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
|
||||
<arg name="active" direction="out" type="b" />
|
||||
</method>
|
||||
<method name="SetActive">
|
||||
<arg name="value" direction="in" type="u" />
|
||||
<arg name="value" direction="in" type="b" />
|
||||
</method>
|
||||
<method name="GetActiveTime">
|
||||
<arg name="value" direction="out" type="u" />
|
||||
</method>
|
||||
<signal name="ActiveChanged">
|
||||
<arg name="new_value" type="b" />
|
||||
@ -340,29 +343,46 @@ const GnomeShellExtensions = new Lang.Class({
|
||||
const ScreenSaverDBus = new Lang.Class({
|
||||
Name: 'ScreenSaverDBus',
|
||||
|
||||
_init: function() {
|
||||
_init: function(screenShield) {
|
||||
this.parent();
|
||||
|
||||
Main.screenShield.connect('lock-status-changed', Lang.bind(this, function(shield, locked) {
|
||||
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [locked]));
|
||||
this._screenShield = screenShield;
|
||||
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
|
||||
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
|
||||
}));
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
|
||||
|
||||
Gio.DBus.session.own_name('org.gnome.ScreenSaver', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
Lock: function() {
|
||||
Main.screenShield.lock(true);
|
||||
LockAsync: function(parameters, invocation) {
|
||||
let tmpId = this._screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
||||
this._screenShield.disconnect(tmpId);
|
||||
|
||||
invocation.return_value(null);
|
||||
}));
|
||||
|
||||
this._screenShield.lock(true);
|
||||
},
|
||||
|
||||
SetActive: function(active) {
|
||||
if (active)
|
||||
Main.screenShield.lock(true);
|
||||
this._screenShield.lock(true);
|
||||
else
|
||||
Main.screenShield.unlock();
|
||||
this._screenShield.unlock();
|
||||
},
|
||||
|
||||
GetActive: function() {
|
||||
return Main.screenShield.locked;
|
||||
}
|
||||
return this._screenShield.locked;
|
||||
},
|
||||
|
||||
GetActiveTime: function() {
|
||||
let started = this._screenShield.activationTime;
|
||||
if (started > 0)
|
||||
return Math.floor((GLib.get_monotonic_time() - started) / 1000000);
|
||||
else
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
|
@ -76,6 +76,12 @@ const EntryMenu = new Lang.Class({
|
||||
this.actor.grab_key_focus();
|
||||
|
||||
this.parent();
|
||||
this._entry.add_style_pseudo_class('focus');
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this._entry.grab_key_focus();
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_updateCopyItem: function() {
|
||||
@ -126,34 +132,20 @@ function _setMenuAlignment(entry, stageX) {
|
||||
entry.menu.setSourceAlignment(entryX / entry.width);
|
||||
};
|
||||
|
||||
function _onClicked(action, actor) {
|
||||
let entry = actor.menu ? actor : actor.get_parent();
|
||||
|
||||
function _onButtonPressEvent(actor, event, entry) {
|
||||
if (entry.menu.isOpen) {
|
||||
entry.menu.close();
|
||||
} else if (action.get_button() == 3) {
|
||||
let [stageX, stageY] = action.get_coords();
|
||||
_setMenuAlignment(entry, stageX);
|
||||
entry.menu.open();
|
||||
}
|
||||
};
|
||||
|
||||
function _onLongPress(action, actor, state) {
|
||||
let entry = actor.menu ? actor : actor.get_parent();
|
||||
|
||||
if (state == Clutter.LongPressState.QUERY)
|
||||
return action.get_button() == 1 && !entry.menu.isOpen;
|
||||
|
||||
if (state == Clutter.LongPressState.ACTIVATE) {
|
||||
let [stageX, stageY] = action.get_coords();
|
||||
return true;
|
||||
} else if (event.get_button() == 3) {
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
_setMenuAlignment(entry, stageX);
|
||||
entry.menu.open();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
function _onPopup(actor) {
|
||||
let entry = actor.menu ? actor : actor.get_parent();
|
||||
function _onPopup(actor, entry) {
|
||||
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
|
||||
if (success)
|
||||
entry.menu.setSourceAlignment(textX / entry.width);
|
||||
@ -168,20 +160,11 @@ function addContextMenu(entry, params) {
|
||||
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
|
||||
entry._menuManager.addMenu(entry.menu);
|
||||
|
||||
let clickAction;
|
||||
|
||||
// Add a click action to both the entry and its clutter_text; the former
|
||||
// Add an event handler to both the entry and its clutter_text; the former
|
||||
// so padding is included in the clickable area, the latter because the
|
||||
// event processing of ClutterText prevents event-bubbling.
|
||||
clickAction = new Clutter.ClickAction();
|
||||
clickAction.connect('clicked', _onClicked);
|
||||
clickAction.connect('long-press', _onLongPress);
|
||||
entry.clutter_text.add_action(clickAction);
|
||||
entry.clutter_text.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
|
||||
entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
|
||||
|
||||
clickAction = new Clutter.ClickAction();
|
||||
clickAction.connect('clicked', _onClicked);
|
||||
clickAction.connect('long-press', _onLongPress);
|
||||
entry.add_action(clickAction);
|
||||
|
||||
entry.connect('popup-menu', _onPopup);
|
||||
entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
|
||||
}
|
||||
|
@ -75,10 +75,6 @@ const ATIndicator = new Lang.Class({
|
||||
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this.actor.visible = !locked;
|
||||
},
|
||||
|
||||
_buildItemExtended: function(string, initial_value, writable, on_set) {
|
||||
let widget = new PopupMenu.PopupSwitchMenuItem(string, initial_value);
|
||||
if (!writable)
|
||||
|
@ -88,11 +88,6 @@ const Indicator = new Lang.Class({
|
||||
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this._isLocked = locked;
|
||||
this._updateKillswitch();
|
||||
},
|
||||
|
||||
_updateKillswitch: function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
|
||||
@ -107,7 +102,7 @@ const Indicator = new Lang.Class({
|
||||
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
|
||||
this._killswitch.setStatus(_("hardware disabled"));
|
||||
|
||||
this.actor.visible = !this._isLocked && has_adapter;
|
||||
this.actor.visible = has_adapter;
|
||||
|
||||
if (on) {
|
||||
this._discoverable.actor.show();
|
||||
@ -461,7 +456,10 @@ const PinNotification = new Lang.Class({
|
||||
|
||||
_canActivateOkButton: function() {
|
||||
// PINs have a fixed length of 6
|
||||
return this._entry.clutter_text.text.length == 6;
|
||||
if (this._numeric)
|
||||
return this._entry.clutter_text.text.length == 6;
|
||||
else
|
||||
return true;
|
||||
},
|
||||
|
||||
grabFocus: function(lockTray) {
|
||||
|
@ -5,6 +5,7 @@ const GLib = imports.gi.GLib;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
try {
|
||||
@ -45,6 +46,7 @@ const IBusManager = new Lang.Class({
|
||||
this._panelService = null;
|
||||
this._engines = {};
|
||||
this._ready = false;
|
||||
this._registerPropertiesId = 0;
|
||||
|
||||
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
@ -63,6 +65,7 @@ const IBusManager = new Lang.Class({
|
||||
this._candidatePopup.setPanelService(null);
|
||||
this._engines = {};
|
||||
this._ready = false;
|
||||
this._registerPropertiesId = 0;
|
||||
},
|
||||
|
||||
_onNameAppeared: function() {
|
||||
@ -100,6 +103,11 @@ const IBusManager = new Lang.Class({
|
||||
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
|
||||
object_path: IBus.PATH_PANEL });
|
||||
this._candidatePopup.setPanelService(this._panelService);
|
||||
// Need to set this to get 'global-engine-changed' emitions
|
||||
this._ibus.set_watch_ibus_signal(true);
|
||||
this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
|
||||
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
|
||||
this._resetProperties();
|
||||
} else {
|
||||
this._clear();
|
||||
return;
|
||||
@ -116,6 +124,39 @@ const IBusManager = new Lang.Class({
|
||||
this._readyCallback();
|
||||
},
|
||||
|
||||
_resetProperties: function() {
|
||||
this.emit('properties-registered', null);
|
||||
|
||||
if (this._registerPropertiesId != 0)
|
||||
return;
|
||||
|
||||
this._registerPropertiesId =
|
||||
this._panelService.connect('register-properties', Lang.bind(this, function(p, props) {
|
||||
if (!props.get(0))
|
||||
return;
|
||||
|
||||
this._panelService.disconnect(this._registerPropertiesId);
|
||||
this._registerPropertiesId = 0;
|
||||
|
||||
this.emit('properties-registered', props);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateProperty: function(panel, prop) {
|
||||
this.emit('property-updated', prop);
|
||||
},
|
||||
|
||||
activateProperty: function(key, state) {
|
||||
this._panelService.property_activate(key, state);
|
||||
},
|
||||
|
||||
hasProperties: function(id) {
|
||||
if (id == 'anthy')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
getEngineDesc: function(id) {
|
||||
if (!IBus || !this._ready)
|
||||
return null;
|
||||
@ -123,6 +164,7 @@ const IBusManager = new Lang.Class({
|
||||
return this._engines[id];
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(IBusManager.prototype);
|
||||
|
||||
const LayoutMenuItem = new Lang.Class({
|
||||
Name: 'LayoutMenuItem',
|
||||
@ -135,6 +177,7 @@ const LayoutMenuItem = new Lang.Class({
|
||||
this.indicator = new St.Label({ text: shortName });
|
||||
this.addActor(this.label);
|
||||
this.addActor(this.indicator);
|
||||
this.actor.label_actor = this.label;
|
||||
}
|
||||
});
|
||||
|
||||
@ -142,6 +185,12 @@ const InputSourceIndicator = new Lang.Class({
|
||||
Name: 'InputSourceIndicator',
|
||||
Extends: PanelMenu.Button,
|
||||
|
||||
_propertiesWhitelist: [
|
||||
'InputMode',
|
||||
'TypingMode',
|
||||
'DictMode'
|
||||
],
|
||||
|
||||
_init: function() {
|
||||
this.parent(0.0, _("Keyboard"));
|
||||
|
||||
@ -162,56 +211,73 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
this._xkbInfo = new GnomeDesktop.XkbInfo();
|
||||
|
||||
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
|
||||
this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(this._propSeparator);
|
||||
this._propSection = new PopupMenu.PopupMenuSection();
|
||||
this.menu.addMenuItem(this._propSection);
|
||||
this._propSection.actor.hide();
|
||||
|
||||
this._properties = null;
|
||||
|
||||
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
|
||||
this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
|
||||
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
|
||||
this._inputSourcesChanged();
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
|
||||
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
// re-using "allowSettings" for the keyboard layout is a bit shady,
|
||||
// but at least for now it is used as "allow popping up windows
|
||||
// from shell menus"; we can always add a separate sessionMode
|
||||
// option if need arises.
|
||||
if (Main.sessionMode.allowSettings) {
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
|
||||
}
|
||||
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
if (Main.sessionMode.allowSettings) {
|
||||
this._showLayoutItem.actor.visible = !locked;
|
||||
}
|
||||
this.menu.setSettingsVisibility(!locked);
|
||||
this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
|
||||
},
|
||||
|
||||
_currentInputSourceChanged: function() {
|
||||
let nVisibleSources = Object.keys(this._layoutItems).length;
|
||||
if (nVisibleSources < 2)
|
||||
return;
|
||||
|
||||
let nSources = this._settings.get_value(KEY_INPUT_SOURCES).n_children();
|
||||
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
if (newCurrentSourceIndex >= nSources)
|
||||
return;
|
||||
let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
|
||||
let hasProperties;
|
||||
|
||||
if (!this._layoutItems[newCurrentSourceIndex]) {
|
||||
// This source index is invalid as we weren't able to
|
||||
// build a menu item for it, so we hide ourselves since we
|
||||
// can't fix it here. *shrug*
|
||||
if (newLayoutItem)
|
||||
hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
|
||||
else
|
||||
hasProperties = false;
|
||||
|
||||
if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
|
||||
// This source index might be invalid if we weren't able
|
||||
// to build a menu item for it, so we hide ourselves since
|
||||
// we can't fix it here. *shrug*
|
||||
|
||||
// We also hide if we have only one visible source unless
|
||||
// it's an IBus source with properties.
|
||||
this.menu.close();
|
||||
this.actor.hide();
|
||||
return;
|
||||
} else {
|
||||
this.actor.show();
|
||||
}
|
||||
|
||||
this.actor.show();
|
||||
|
||||
if (this._layoutItems[this._currentSourceIndex]) {
|
||||
this._layoutItems[this._currentSourceIndex].setShowDot(false);
|
||||
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
|
||||
}
|
||||
|
||||
this._layoutItems[newCurrentSourceIndex].setShowDot(true);
|
||||
this._container.set_skip_paint(this._labelActors[newCurrentSourceIndex], false);
|
||||
newLayoutItem.setShowDot(true);
|
||||
|
||||
let newLabelActor = this._labelActors[newCurrentSourceIndex];
|
||||
this._container.set_skip_paint(newLabelActor, false);
|
||||
|
||||
if (hasProperties)
|
||||
newLabelActor.set_text(newLayoutItem.indicator.get_text());
|
||||
|
||||
this._currentSourceIndex = newCurrentSourceIndex;
|
||||
},
|
||||
@ -242,9 +308,12 @@ const InputSourceIndicator = new Lang.Class({
|
||||
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
|
||||
let engineDesc = this._ibusManager.getEngineDesc(id);
|
||||
if (engineDesc) {
|
||||
let language = IBus.get_language_name(engineDesc.get_language());
|
||||
|
||||
info.exists = true;
|
||||
info.displayName = engineDesc.get_longname();
|
||||
info.shortName = engineDesc.get_symbol();
|
||||
info.displayName = language + ' (' + engineDesc.get_longname() + ')';
|
||||
info.shortName = this._makeEngineShortName(engineDesc);
|
||||
info.ibusEngineId = id;
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,13 +328,6 @@ const InputSourceIndicator = new Lang.Class({
|
||||
infos.push(info);
|
||||
}
|
||||
|
||||
if (infos.length > 1) {
|
||||
this.actor.show();
|
||||
} else {
|
||||
this.menu.close();
|
||||
this.actor.hide();
|
||||
}
|
||||
|
||||
for (let i = 0; i < infos.length; i++) {
|
||||
let info = infos[i];
|
||||
if (infosByShortName[info.shortName].length > 1) {
|
||||
@ -274,6 +336,7 @@ const InputSourceIndicator = new Lang.Class({
|
||||
}
|
||||
|
||||
let item = new LayoutMenuItem(info.displayName, info.shortName);
|
||||
item.ibusEngineId = info.ibusEngineId;
|
||||
this._layoutItems[info.sourceIndex] = item;
|
||||
this.menu.addMenuItem(item, i);
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
@ -319,6 +382,146 @@ const InputSourceIndicator = new Lang.Class({
|
||||
Util.spawn(['gkbd-keyboard-display', '-l', description]);
|
||||
},
|
||||
|
||||
_makeEngineShortName: function(engineDesc) {
|
||||
let symbol = engineDesc.get_symbol();
|
||||
if (symbol && symbol[0])
|
||||
return symbol;
|
||||
|
||||
let langCode = engineDesc.get_language().split('_', 1)[0];
|
||||
if (langCode.length == 2 || langCode.length == 3)
|
||||
return langCode.toLowerCase();
|
||||
|
||||
return String.fromCharCode(0x2328); // keyboard glyph
|
||||
},
|
||||
|
||||
_propertyWhitelisted: function(prop) {
|
||||
for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
|
||||
let key = prop.get_key();
|
||||
if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_ibusPropertiesRegistered: function(im, props) {
|
||||
this._properties = props;
|
||||
this._buildPropSection();
|
||||
},
|
||||
|
||||
_ibusPropertyUpdated: function(im, prop) {
|
||||
if (!this._propertyWhitelisted(prop))
|
||||
return;
|
||||
|
||||
if (this._updateSubProperty(this._properties, prop))
|
||||
this._buildPropSection();
|
||||
},
|
||||
|
||||
_updateSubProperty: function(props, prop) {
|
||||
if (!props)
|
||||
return false;
|
||||
|
||||
let p;
|
||||
for (let i = 0; (p = props.get(i)) != null; ++i) {
|
||||
if (p.get_key() == prop.get_key() && p.get_prop_type() == prop.get_prop_type()) {
|
||||
p.update(prop);
|
||||
return true;
|
||||
} else if (p.get_prop_type() == IBus.PropType.MENU) {
|
||||
if (this._updateSubProperty(p.get_sub_props(), prop))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_updateIndicatorLabel: function(text) {
|
||||
let layoutItem = this._layoutItems[this._currentSourceIndex];
|
||||
let hasProperties;
|
||||
|
||||
if (layoutItem)
|
||||
hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
|
||||
else
|
||||
hasProperties = false;
|
||||
|
||||
if (hasProperties)
|
||||
this._labelActors[this._currentSourceIndex].set_text(text);
|
||||
},
|
||||
|
||||
_buildPropSection: function() {
|
||||
this._propSeparator.actor.hide();
|
||||
this._propSection.actor.hide();
|
||||
this._propSection.removeAll();
|
||||
|
||||
this._buildPropSubMenu(this._propSection, this._properties);
|
||||
|
||||
if (!this._propSection.isEmpty()) {
|
||||
this._propSection.actor.show();
|
||||
this._propSeparator.actor.show();
|
||||
}
|
||||
},
|
||||
|
||||
_buildPropSubMenu: function(menu, props) {
|
||||
if (!props)
|
||||
return;
|
||||
|
||||
let radioGroup = [];
|
||||
let p;
|
||||
for (let i = 0; (p = props.get(i)) != null; ++i) {
|
||||
let prop = p;
|
||||
|
||||
if (!this._propertyWhitelisted(prop) ||
|
||||
!prop.get_visible())
|
||||
continue;
|
||||
|
||||
if (prop.get_key() == 'InputMode') {
|
||||
let text;
|
||||
if (prop.get_symbol)
|
||||
text = prop.get_symbol().get_text();
|
||||
else
|
||||
text = prop.get_label().get_text();
|
||||
|
||||
if (text && text.length > 0 && text.length < 3)
|
||||
this._updateIndicatorLabel(text);
|
||||
}
|
||||
|
||||
let item;
|
||||
let type = prop.get_prop_type();
|
||||
if (type == IBus.PropType.MENU) {
|
||||
item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
|
||||
this._buildPropSubMenu(item.menu, prop.get_sub_props());
|
||||
} else if (type == IBus.PropType.RADIO) {
|
||||
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
|
||||
item.prop = prop;
|
||||
radioGroup.push(item);
|
||||
item.radioGroup = radioGroup;
|
||||
item.setShowDot(prop.get_state() == IBus.PropState.CHECKED);
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
if (item.prop.get_state() == IBus.PropState.CHECKED)
|
||||
return;
|
||||
|
||||
let group = item.radioGroup;
|
||||
for (let i = 0; i < group.length; ++i) {
|
||||
if (group[i] == item) {
|
||||
item.setShowDot(true);
|
||||
item.prop.set_state(IBus.PropState.CHECKED);
|
||||
this._ibusManager.activateProperty(item.prop.get_key(),
|
||||
IBus.PropState.CHECKED);
|
||||
} else {
|
||||
group[i].setShowDot(false);
|
||||
group[i].prop.set_state(IBus.PropState.UNCHECKED);
|
||||
this._ibusManager.activateProperty(group[i].prop.get_key(),
|
||||
IBus.PropState.UNCHECKED);
|
||||
}
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
item.setSensitive(prop.get_sensitive());
|
||||
menu.addMenuItem(item);
|
||||
}
|
||||
},
|
||||
|
||||
_containerGetPreferredWidth: function(container, for_height, alloc) {
|
||||
// Here, and in _containerGetPreferredHeight, we need to query
|
||||
// for the height of all children, but we ignore the results
|
||||
|
@ -16,7 +16,6 @@ const Indicator = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this.parent(null, _("Volume, network, battery"));
|
||||
this.actor.hide();
|
||||
|
||||
this._volume = Main.panel.statusArea.volume;
|
||||
if (this._volume) {
|
||||
@ -54,9 +53,5 @@ const Indicator = new Lang.Class({
|
||||
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this.actor.visible = locked;
|
||||
}
|
||||
});
|
||||
|
@ -233,20 +233,11 @@ const NMWirelessSectionTitleMenuItem = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const NMDevice = new Lang.Class({
|
||||
Name: 'NMDevice',
|
||||
const NMConnectionBased = new Lang.Class({
|
||||
Name: 'NMConnectionBased',
|
||||
Abstract: true,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
this.device = device;
|
||||
if (device) {
|
||||
this.device._delegate = this;
|
||||
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
|
||||
} else
|
||||
this._stateChangedId = 0;
|
||||
|
||||
// protected
|
||||
this._client = client;
|
||||
_init: function(connections) {
|
||||
this._connections = [ ];
|
||||
for (let i = 0; i < connections.length; i++) {
|
||||
if (!connections[i].get_uuid())
|
||||
@ -264,26 +255,115 @@ const NMDevice = new Lang.Class({
|
||||
this._connections.push(obj);
|
||||
}
|
||||
this._connections.sort(this._connectionSortFunction);
|
||||
},
|
||||
|
||||
checkConnection: function(connection) {
|
||||
let pos = this._findConnection(connection.get_uuid());
|
||||
let exists = pos != -1;
|
||||
let valid = this.connectionValid(connection);
|
||||
let similar = false;
|
||||
if (exists) {
|
||||
let existing = this._connections[pos];
|
||||
|
||||
// Check if connection changed name or id
|
||||
similar = existing.name == connection.get_id() &&
|
||||
existing.timestamp == connection._timestamp;
|
||||
}
|
||||
|
||||
if (exists && valid && similar) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (exists)
|
||||
this.removeConnection(connection);
|
||||
if (valid)
|
||||
this.addConnection(connection);
|
||||
},
|
||||
|
||||
addConnection: function(connection) {
|
||||
// record the connection
|
||||
let obj = {
|
||||
connection: connection,
|
||||
name: connection.get_id(),
|
||||
uuid: connection.get_uuid(),
|
||||
timestamp: connection._timestamp,
|
||||
item: null,
|
||||
};
|
||||
Util.insertSorted(this._connections, obj, this._connectionSortFunction);
|
||||
|
||||
this._queueCreateSection();
|
||||
},
|
||||
|
||||
removeConnection: function(connection) {
|
||||
let pos = this._findConnection(connection.get_uuid());
|
||||
if (pos == -1) {
|
||||
// this connection was never added, nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = this._connections[pos];
|
||||
if (obj.item)
|
||||
obj.item.destroy();
|
||||
this._connections.splice(pos, 1);
|
||||
|
||||
if (this._connections.length <= 1) {
|
||||
// We need to show the automatic connection again
|
||||
// (or in the case of NMDeviceWired, we want to hide
|
||||
// the only explicit connection)
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
_findConnection: function(uuid) {
|
||||
for (let i = 0; i < this._connections.length; i++) {
|
||||
let obj = this._connections[i];
|
||||
if (obj.uuid == uuid)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
_connectionSortFunction: function(one, two) {
|
||||
if (one.timestamp == two.timestamp)
|
||||
return GLib.utf8_collate(one.name, two.name);
|
||||
|
||||
return two.timestamp - one.timestamp;
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(NMConnectionBased.prototype);
|
||||
|
||||
const NMDevice = new Lang.Class({
|
||||
Name: 'NMDevice',
|
||||
Abstract: true,
|
||||
Extends: NMConnectionBased,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
this.device = device;
|
||||
this.device._delegate = this;
|
||||
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
|
||||
|
||||
// protected
|
||||
this._client = client;
|
||||
this.parent(connections);
|
||||
this._activeConnection = null;
|
||||
this._activeConnectionItem = null;
|
||||
this._autoConnectionItem = null;
|
||||
this._overflowItem = null;
|
||||
|
||||
if (this.device) {
|
||||
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
|
||||
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
|
||||
let ok;
|
||||
if (state)
|
||||
ok = this.activate();
|
||||
else
|
||||
ok = this.deactivate();
|
||||
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
|
||||
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
|
||||
let ok;
|
||||
if (state)
|
||||
ok = this.activate();
|
||||
else
|
||||
ok = this.deactivate();
|
||||
|
||||
if (!ok)
|
||||
item.setToggleState(!state);
|
||||
}));
|
||||
if (!ok)
|
||||
item.setToggleState(!state);
|
||||
}));
|
||||
|
||||
this._updateStatusItem();
|
||||
}
|
||||
this._updateStatusItem();
|
||||
this.section = new PopupMenu.PopupMenuSection();
|
||||
|
||||
this._deferredWorkId = Main.initializeDeferredWork(this.section.actor, Lang.bind(this, this._createSection));
|
||||
@ -352,6 +432,10 @@ const NMDevice = new Lang.Class({
|
||||
return this.device.state == NetworkManager.DeviceState.ACTIVATED;
|
||||
},
|
||||
|
||||
clearActiveConnection: function(activeConnection) {
|
||||
this.setActiveConnection(null);
|
||||
},
|
||||
|
||||
setActiveConnection: function(activeConnection) {
|
||||
if (activeConnection == this._activeConnection)
|
||||
// nothing to do
|
||||
@ -369,80 +453,13 @@ const NMDevice = new Lang.Class({
|
||||
this._queueCreateSection();
|
||||
},
|
||||
|
||||
checkConnection: function(connection) {
|
||||
let pos = this._findConnection(connection.get_uuid());
|
||||
let exists = pos != -1;
|
||||
let valid = this.connectionValid(connection);
|
||||
let similar = false;
|
||||
if (exists) {
|
||||
let existing = this._connections[pos];
|
||||
|
||||
// Check if connection changed name or id
|
||||
similar = existing.name == connection.get_id() &&
|
||||
existing.timestamp == connection._timestamp;
|
||||
}
|
||||
|
||||
if (exists && valid && similar) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (exists)
|
||||
this.removeConnection(connection);
|
||||
if (valid)
|
||||
this.addConnection(connection);
|
||||
},
|
||||
|
||||
addConnection: function(connection) {
|
||||
// record the connection
|
||||
let obj = {
|
||||
connection: connection,
|
||||
name: connection.get_id(),
|
||||
uuid: connection.get_uuid(),
|
||||
timestamp: connection._timestamp,
|
||||
item: null,
|
||||
};
|
||||
Util.insertSorted(this._connections, obj, this._connectionSortFunction);
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
},
|
||||
|
||||
removeConnection: function(connection) {
|
||||
let pos = this._findConnection(connection.get_uuid());
|
||||
if (pos == -1) {
|
||||
// this connection was never added, nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = this._connections[pos];
|
||||
if (obj.item)
|
||||
obj.item.destroy();
|
||||
this._connections.splice(pos, 1);
|
||||
|
||||
if (this._connections.length <= 1) {
|
||||
// We need to show the automatic connection again
|
||||
// (or in the case of NMDeviceWired, we want to hide
|
||||
// the only explicit connection)
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
connectionValid: function(connection) {
|
||||
return this.device.connection_valid(connection);
|
||||
},
|
||||
|
||||
_connectionSortFunction: function(one, two) {
|
||||
if (one.timestamp == two.timestamp)
|
||||
return GLib.utf8_collate(one.name, two.name);
|
||||
|
||||
return two.timestamp - one.timestamp;
|
||||
},
|
||||
|
||||
setEnabled: function(enabled) {
|
||||
// do nothing by default, we want to keep the conneciton list visible
|
||||
// in the majority of cases (wired, wwan, vpn)
|
||||
// in the majority of cases (wired, wwan)
|
||||
},
|
||||
|
||||
getStatusLabel: function() {
|
||||
@ -500,15 +517,6 @@ const NMDevice = new Lang.Class({
|
||||
throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection');
|
||||
},
|
||||
|
||||
_findConnection: function(uuid) {
|
||||
for (let i = 0; i < this._connections.length; i++) {
|
||||
let obj = this._connections[i];
|
||||
if (obj.uuid == uuid)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
_queueCreateSection: function() {
|
||||
this._clearSection();
|
||||
Main.queueDeferredWork(this._deferredWorkId);
|
||||
@ -652,8 +660,6 @@ const NMDevice = new Lang.Class({
|
||||
return out;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(NMDevice.prototype);
|
||||
|
||||
|
||||
const NMDeviceWired = new Lang.Class({
|
||||
Name: 'NMDeviceWired',
|
||||
@ -868,95 +874,6 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Not a real device, but I save a lot code this way
|
||||
const NMDeviceVPN = new Lang.Class({
|
||||
Name: 'NMDeviceVPN',
|
||||
Extends: NMDevice,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
// Disable autoconnections
|
||||
this._autoConnectionName = null;
|
||||
this.category = NMConnectionCategory.VPN;
|
||||
|
||||
this.parent(client, null, connections);
|
||||
},
|
||||
|
||||
connectionValid: function(connection) {
|
||||
return connection._type == NetworkManager.SETTING_VPN_SETTING_NAME;
|
||||
},
|
||||
|
||||
get empty() {
|
||||
return this._connections.length == 0;
|
||||
},
|
||||
|
||||
get connected() {
|
||||
if (!this._activeConnection)
|
||||
return false;
|
||||
|
||||
return this._activeConnection.vpn_state == NetworkManager.VPNConnectionState.ACTIVATED;
|
||||
},
|
||||
|
||||
setActiveConnection: function(activeConnection) {
|
||||
if (this._stateChangeId)
|
||||
this._activeConnection.disconnect(this._stateChangeId);
|
||||
this._stateChangeId = 0;
|
||||
|
||||
this.parent(activeConnection);
|
||||
|
||||
if (this._activeConnection)
|
||||
this._stateChangeId = this._activeConnection.connect('vpn-state-changed',
|
||||
Lang.bind(this, this._connectionStateChanged));
|
||||
|
||||
this.emit('state-changed');
|
||||
},
|
||||
|
||||
_shouldShowConnectionList: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
if (this._activeConnection)
|
||||
this._client.deactivate_connection(this._activeConnection);
|
||||
},
|
||||
|
||||
getStatusLabel: function() {
|
||||
if (!this._activeConnection) // Same as DISCONNECTED
|
||||
return null;
|
||||
|
||||
switch(this._activeConnection.vpn_state) {
|
||||
case NetworkManager.VPNConnectionState.DISCONNECTED:
|
||||
case NetworkManager.VPNConnectionState.ACTIVATED:
|
||||
return null;
|
||||
case NetworkManager.VPNConnectionState.PREPARE:
|
||||
case NetworkManager.VPNConnectionState.CONNECT:
|
||||
case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
|
||||
return _("connecting...");
|
||||
case NetworkManager.VPNConnectionState.NEED_AUTH:
|
||||
/* Translators: this is for network connections that require some kind of key or password */
|
||||
return _("authentication required");
|
||||
case NetworkManager.VPNConnectionState.FAILED:
|
||||
return _("connection failed");
|
||||
default:
|
||||
log('VPN connection state invalid, is %d'.format(this.device.state));
|
||||
return 'invalid';
|
||||
}
|
||||
},
|
||||
|
||||
_connectionStateChanged: function(connection, newstate, reason) {
|
||||
if (newstate == NetworkManager.VPNConnectionState.FAILED) {
|
||||
// FIXME: if we ever want to show something based on reason,
|
||||
// we need to convert from NetworkManager.VPNConnectionStateReason
|
||||
// to NetworkManager.DeviceStateReason
|
||||
this.emit('activation-failed', reason);
|
||||
}
|
||||
|
||||
// Differently from real NMDevices, there is no need to queue
|
||||
// an update of the menu section, contents wouldn't change anyway
|
||||
this.emit('state-changed');
|
||||
}
|
||||
});
|
||||
|
||||
const NMDeviceWireless = new Lang.Class({
|
||||
Name: 'NMDeviceWireless',
|
||||
Extends: NMDevice,
|
||||
@ -1557,21 +1474,165 @@ const NMDeviceWireless = new Lang.Class({
|
||||
},
|
||||
});
|
||||
|
||||
const NMVPNSection = new Lang.Class({
|
||||
Name: 'NMVPNSection',
|
||||
Extends: NMConnectionBased,
|
||||
category: NMConnectionCategory.VPN,
|
||||
|
||||
_init: function(client, connections) {
|
||||
this.parent(connections);
|
||||
this._client = client;
|
||||
|
||||
this.section = new PopupMenu.PopupMenuSection();
|
||||
this._deferredWorkId = Main.initializeDeferredWork(this.section.actor, Lang.bind(this, this._createSection));
|
||||
},
|
||||
|
||||
get empty() {
|
||||
return this._connections.length == 0;
|
||||
},
|
||||
|
||||
connectionValid: function(connection) {
|
||||
// filtering is done by NMApplet code
|
||||
return true;
|
||||
},
|
||||
|
||||
getStatusLabel: function(activeConnection) {
|
||||
switch(activeConnection.vpn_state) {
|
||||
case NetworkManager.VPNConnectionState.DISCONNECTED:
|
||||
case NetworkManager.VPNConnectionState.ACTIVATED:
|
||||
return null;
|
||||
case NetworkManager.VPNConnectionState.PREPARE:
|
||||
case NetworkManager.VPNConnectionState.CONNECT:
|
||||
case NetworkManager.VPNConnectionState.IP_CONFIG_GET:
|
||||
return _("connecting...");
|
||||
case NetworkManager.VPNConnectionState.NEED_AUTH:
|
||||
/* Translators: this is for network connections that require some kind of key or password */
|
||||
return _("authentication required");
|
||||
case NetworkManager.VPNConnectionState.FAILED:
|
||||
return _("connection failed");
|
||||
default:
|
||||
log('VPN connection state invalid, is %d'.format(this.device.state));
|
||||
return 'invalid';
|
||||
}
|
||||
},
|
||||
|
||||
clearActiveConnection: function(activeConnection) {
|
||||
let pos = this._findConnection(activeConnection.uuid);
|
||||
if (pos < 0)
|
||||
return;
|
||||
|
||||
let obj = this._connections[pos];
|
||||
obj.active.disconnect(obj.stateChangedId);
|
||||
obj.active = null;
|
||||
|
||||
if (obj.item) {
|
||||
obj.item.setToggleState(false);
|
||||
obj.item.setStatus(null);
|
||||
}
|
||||
},
|
||||
|
||||
setActiveConnection: function(activeConnection) {
|
||||
let pos = this._findConnection(activeConnection.uuid);
|
||||
if (pos < 0)
|
||||
return;
|
||||
|
||||
let obj = this._connections[pos];
|
||||
obj.active = activeConnection;
|
||||
obj.stateChangedId = obj.active.connect('vpn-state-changed',
|
||||
Lang.bind(this, this._connectionStateChanged));
|
||||
|
||||
if (obj.item) {
|
||||
obj.item.setToggleState(obj.active.vpn_state ==
|
||||
NetworkManager.VPNConnectionState.ACTIVATED);
|
||||
obj.item.setStatus(this.getStatusLabel(obj.active));
|
||||
}
|
||||
},
|
||||
|
||||
_queueCreateSection: function() {
|
||||
this.section.removeAll();
|
||||
Main.queueDeferredWork(this._deferredWorkId);
|
||||
},
|
||||
|
||||
_createConnectionItem: function(obj) {
|
||||
let menuItem = new PopupMenu.PopupSwitchMenuItem(obj.name, false,
|
||||
{ style_class: 'popup-subtitle-menu-item' });
|
||||
menuItem.connect('toggled', Lang.bind(this, function(menuItem) {
|
||||
if (menuItem.state) {
|
||||
this._client.activate_connection(obj.connection, null, null, null);
|
||||
// Immediately go back to disconnected, until NM tells us to change
|
||||
menuItem.setToggleState(false);
|
||||
} else if (obj.active) {
|
||||
this._client.deactivate_connection(obj.active);
|
||||
}
|
||||
}));
|
||||
|
||||
if (obj.active) {
|
||||
menuItem.setToggleState(obj.active.vpn_state ==
|
||||
NetworkManager.VPNConnectionState.ACTIVATED);
|
||||
menuItem.setStatus(this.getStatusLabel(obj.active));
|
||||
}
|
||||
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
_createSection: function() {
|
||||
if (this._connections.length > 0) {
|
||||
this.section.actor.show();
|
||||
|
||||
for(let j = 0; j < this._connections.length; ++j) {
|
||||
let obj = this._connections[j];
|
||||
obj.item = this._createConnectionItem(obj);
|
||||
|
||||
if (j >= NUM_VISIBLE_NETWORKS) {
|
||||
if (!this._overflowItem) {
|
||||
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
|
||||
this.section.addMenuItem(this._overflowItem);
|
||||
}
|
||||
this._overflowItem.menu.addMenuItem(obj.item);
|
||||
} else
|
||||
this.section.addMenuItem(obj.item);
|
||||
}
|
||||
} else {
|
||||
this.section.actor.hide()
|
||||
}
|
||||
},
|
||||
|
||||
_connectionStateChanged: function(vpnConnection, newstate, reason) {
|
||||
if (newstate == NetworkManager.VPNConnectionState.FAILED) {
|
||||
// FIXME: if we ever want to show something based on reason,
|
||||
// we need to convert from NetworkManager.VPNConnectionStateReason
|
||||
// to NetworkManager.DeviceStateReason
|
||||
this.emit('activation-failed', reason);
|
||||
}
|
||||
|
||||
let pos = this._findConnection(vpnConnection.uuid);
|
||||
if (pos >= 0) {
|
||||
let obj = this._connections[pos];
|
||||
if (obj.item) {
|
||||
obj.item.setToggleState(vpnConnection.vpn_state ==
|
||||
NetworkManager.VPNConnectionState.ACTIVATED);
|
||||
obj.item.setStatus(this.getStatusLabel(vpnConnection));
|
||||
}
|
||||
} else {
|
||||
log('Could not find connection for vpn-state-changed handler');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const NMApplet = new Lang.Class({
|
||||
Name: 'NMApplet',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
|
||||
_init: function() {
|
||||
this.parent('network-offline', _('Network'));
|
||||
this.parent('network-offline-symbolic', _('Network'));
|
||||
|
||||
this.secondaryIcon = this.addIcon(new Gio.ThemedIcon({ name: 'network-vpn-symbolic' }));
|
||||
this.secondaryIcon.hide();
|
||||
|
||||
this._isLocked = false;
|
||||
this._client = NMClient.Client.new();
|
||||
|
||||
this._statusSection = new PopupMenu.PopupMenuSection();
|
||||
this._statusItem = new PopupMenu.PopupMenuItem('', { style_class: 'popup-inactive-menu-item', reactive: false });
|
||||
this._statusItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._statusSection.addMenuItem(this._statusItem);
|
||||
this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
|
||||
this._client.networking_enabled = true;
|
||||
@ -1623,15 +1684,9 @@ const NMApplet = new Lang.Class({
|
||||
this.menu.addMenuItem(this._devices.wwan.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices.vpn = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
device: this._makeWrapperDevice(NMDeviceVPN, null),
|
||||
item: new NMWiredSectionTitleMenuItem(_("VPN Connections"))
|
||||
};
|
||||
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
|
||||
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
|
||||
this._devices.vpn.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.vpn.section);
|
||||
this._vpnSection = new NMVPNSection(this._client, this._connections);
|
||||
this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed));
|
||||
this.menu.addMenuItem(this._vpnSection.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
|
||||
|
||||
@ -1675,11 +1730,6 @@ const NMApplet = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this._isLocked = locked;
|
||||
this._syncNMState();
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new MessageTray.Source(_("Network Manager"),
|
||||
@ -1705,18 +1755,6 @@ const NMApplet = new Lang.Class({
|
||||
},
|
||||
|
||||
_syncSectionTitle: function(category) {
|
||||
if (category == NMConnectionCategory.VPN) {
|
||||
// Special case VPN: it's only one device (and a fake one
|
||||
// actually), and we don't show it if empty
|
||||
let device = this._devices.vpn.device;
|
||||
let section = this._devices.vpn.section;
|
||||
let item = this._devices.vpn.item;
|
||||
|
||||
section.actor.visible = !device.empty;
|
||||
item.updateForDevice(device);
|
||||
return;
|
||||
}
|
||||
|
||||
let devices = this._devices[category].devices;
|
||||
let item = this._devices[category].item;
|
||||
let section = this._devices[category].section;
|
||||
@ -1753,10 +1791,9 @@ const NMApplet = new Lang.Class({
|
||||
or this._source will be cleared */
|
||||
this._ensureSource();
|
||||
|
||||
let icon = new St.Icon({ icon_name: iconName,
|
||||
icon_size: MessageTray.NOTIFICATION_ICON_SIZE });
|
||||
let gicon = new Gio.ThemedIcon({ name: iconName });
|
||||
device._notification = new MessageTray.Notification(this._source, title, text,
|
||||
{ icon: icon });
|
||||
{ gicon: gicon });
|
||||
device._notification.setUrgency(urgency);
|
||||
device._notification.setTransient(true);
|
||||
device._notification.connect('destroy', function() {
|
||||
@ -1765,17 +1802,20 @@ const NMApplet = new Lang.Class({
|
||||
this._source.notify(device._notification);
|
||||
},
|
||||
|
||||
_onActivationFailed: function(device, reason) {
|
||||
// XXX: nm-applet has no special text depending on reason
|
||||
// but I'm not sure of this generic message
|
||||
this._notifyForDevice(device, 'network-error-symbolic',
|
||||
_("Connection failed"),
|
||||
_("Activation of network connection failed"),
|
||||
MessageTray.Urgency.HIGH);
|
||||
},
|
||||
|
||||
_makeWrapperDevice: function(wrapperClass, device) {
|
||||
let wrapper = new wrapperClass(this._client, device, this._connections);
|
||||
|
||||
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
|
||||
// XXX: nm-applet has no special text depending on reason
|
||||
// but I'm not sure of this generic message
|
||||
this._notifyForDevice(device, 'network-error-symbolic',
|
||||
_("Connection failed"),
|
||||
_("Activation of network connection failed"),
|
||||
MessageTray.Urgency.HIGH);
|
||||
}));
|
||||
wrapper._activationFailedId = wrapper.connect('activation-failed',
|
||||
Lang.bind(this, this._onActivationFailed));
|
||||
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
|
||||
this._syncSectionTitle(dev.category);
|
||||
}));
|
||||
@ -1858,7 +1898,7 @@ const NMApplet = new Lang.Class({
|
||||
for (let i = 0; i < closedConnections.length; i++) {
|
||||
let active = closedConnections[i];
|
||||
if (active._primaryDevice) {
|
||||
active._primaryDevice.setActiveConnection(null);
|
||||
active._primaryDevice.clearActiveConnection(active);
|
||||
active._primaryDevice = null;
|
||||
}
|
||||
if (active._inited) {
|
||||
@ -1925,7 +1965,7 @@ const NMApplet = new Lang.Class({
|
||||
}
|
||||
}
|
||||
} else
|
||||
a._primaryDevice = this._devices.vpn.device
|
||||
a._primaryDevice = this._vpnSection;
|
||||
|
||||
if (a._primaryDevice)
|
||||
a._primaryDevice.setActiveConnection(a);
|
||||
@ -2007,8 +2047,7 @@ const NMApplet = new Lang.Class({
|
||||
let section = connection._section;
|
||||
|
||||
if (section == NMConnectionCategory.VPN) {
|
||||
this._devices.vpn.device.removeConnection(connection);
|
||||
this._syncSectionTitle(section);
|
||||
this._vpnSection.removeConnection(connection);
|
||||
} else if (section != NMConnectionCategory.INVALID) {
|
||||
let devices = this._devices[section].devices;
|
||||
for (let i = 0; i < devices.length; i++)
|
||||
@ -2031,8 +2070,7 @@ const NMApplet = new Lang.Class({
|
||||
if (section == NMConnectionCategory.INVALID)
|
||||
return;
|
||||
if (section == NMConnectionCategory.VPN) {
|
||||
this._devices.vpn.device.checkConnection(connection);
|
||||
this._syncSectionTitle(section);
|
||||
this._vpnSection.checkConnection(connection);
|
||||
} else {
|
||||
let devices = this._devices[section].devices;
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
@ -2058,12 +2096,11 @@ const NMApplet = new Lang.Class({
|
||||
this._syncSectionTitle(NMConnectionCategory.WIRED);
|
||||
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
|
||||
this._syncSectionTitle(NMConnectionCategory.WWAN);
|
||||
this._syncSectionTitle(NMConnectionCategory.VPN);
|
||||
},
|
||||
|
||||
_syncNMState: function() {
|
||||
this.mainIcon.visible = this._client.manager_running;
|
||||
this.actor.visible = this.mainIcon.visible && !this._isLocked;
|
||||
this.actor.visible = this.mainIcon.visible;
|
||||
|
||||
if (!this._client.networking_enabled) {
|
||||
this.setIcon('network-offline-symbolic');
|
||||
|
@ -56,7 +56,6 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
|
||||
|
||||
this._isLocked = false;
|
||||
this._deviceItems = [ ];
|
||||
this._hasPrimary = false;
|
||||
this._primaryDeviceId = null;
|
||||
@ -77,11 +76,6 @@ const Indicator = new Lang.Class({
|
||||
this._devicesChanged();
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this._isLocked = locked;
|
||||
this._syncIcon();
|
||||
},
|
||||
|
||||
_readPrimaryDevice: function() {
|
||||
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
|
||||
if (error) {
|
||||
@ -160,7 +154,7 @@ const Indicator = new Lang.Class({
|
||||
hasIcon = true;
|
||||
}
|
||||
this.mainIcon.visible = hasIcon;
|
||||
this.actor.visible = hasIcon && !this._isLocked;
|
||||
this.actor.visible = hasIcon;
|
||||
},
|
||||
|
||||
_devicesChanged: function() {
|
||||
|
@ -62,6 +62,8 @@ const VolumeMenu = new Lang.Class({
|
||||
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.addMenuItem(this._inputTitle);
|
||||
this.addMenuItem(this._inputSlider);
|
||||
|
||||
this._onControlStateChanged();
|
||||
},
|
||||
|
||||
scroll: function(direction) {
|
||||
@ -90,6 +92,7 @@ const VolumeMenu = new Lang.Class({
|
||||
if (this._control.get_state() == Gvc.MixerControlState.READY) {
|
||||
this._readOutput();
|
||||
this._readInput();
|
||||
this._maybeShowInput();
|
||||
} else {
|
||||
this.emit('icon-changed', null);
|
||||
}
|
||||
@ -217,8 +220,6 @@ const Indicator = new Lang.Class({
|
||||
_init: function() {
|
||||
this.parent('audio-volume-muted-symbolic', _("Volume"));
|
||||
|
||||
this._isLocked = false;
|
||||
|
||||
this._control = getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu(this._control);
|
||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
|
||||
@ -235,13 +236,8 @@ const Indicator = new Lang.Class({
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this._isLocked = locked;
|
||||
this._syncVisibility();
|
||||
},
|
||||
|
||||
_syncVisibility: function() {
|
||||
this.actor.visible = this._hasPulseAudio && !this._isLocked;
|
||||
this.actor.visible = this._hasPulseAudio;
|
||||
this.mainIcon.visible = this._hasPulseAudio;
|
||||
},
|
||||
|
||||
|
@ -2,9 +2,10 @@
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
@ -23,6 +24,35 @@ const GdmUtil = imports.gdm.util;
|
||||
// The timeout before going back automatically to the lock screen (in seconds)
|
||||
const IDLE_TIMEOUT = 2 * 60;
|
||||
|
||||
function versionCompare(required, reference) {
|
||||
required = required.split('.');
|
||||
reference = reference.split('.');
|
||||
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
if (required[i] != reference[i])
|
||||
return required[i] < reference[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isSupported() {
|
||||
try {
|
||||
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
|
||||
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
|
||||
'/org/gnome/DisplayManager/Manager',
|
||||
'org.freedesktop.DBus.Properties',
|
||||
'Get', params, null,
|
||||
Gio.DBusCallFlags.NONE,
|
||||
-1, null);
|
||||
|
||||
let version = result.deep_unpack()[0].deep_unpack();
|
||||
return versionCompare('3.5.91', version);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// A widget showing the user avatar and name
|
||||
const UserWidget = new Lang.Class({
|
||||
Name: 'UserWidget',
|
||||
@ -41,6 +71,7 @@ const UserWidget = new Lang.Class({
|
||||
this.actor.add(this._label,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: false,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded',
|
||||
@ -89,13 +120,17 @@ const UnlockDialog = new Lang.Class({
|
||||
this._userName = GLib.get_user_name();
|
||||
this._user = this._userManager.get_user(this._userName);
|
||||
|
||||
this._failCounter = 0;
|
||||
this._firstQuestion = true;
|
||||
|
||||
this._greeterClient = new Gdm.Client();
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
|
||||
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
||||
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
|
||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||
|
||||
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
|
||||
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
|
||||
@ -112,7 +147,8 @@ const UnlockDialog = new Lang.Class({
|
||||
|
||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
ShellEntry.addContextMenu(this._promptEntry);
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
|
||||
this.setInitialKeyFocus(this._promptEntry);
|
||||
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
|
||||
|
||||
@ -122,6 +158,9 @@ const UnlockDialog = new Lang.Class({
|
||||
|
||||
this.contentLayout.add_actor(this._promptLayout);
|
||||
|
||||
this._promptMessage = new St.Label({ visible: false });
|
||||
this.contentLayout.add(this._promptMessage, { x_fill: true });
|
||||
|
||||
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint' });
|
||||
this._promptLoginHint.hide();
|
||||
this.contentLayout.add_actor(this._promptLoginHint);
|
||||
@ -134,8 +173,7 @@ const UnlockDialog = new Lang.Class({
|
||||
default: true };
|
||||
this.setButtons([cancelButton, this._okButton]);
|
||||
|
||||
this._updateOkButton(false);
|
||||
this._reset();
|
||||
this._updateSensitivity(true);
|
||||
|
||||
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
@ -150,35 +188,55 @@ const UnlockDialog = new Lang.Class({
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
|
||||
let batch = new Batch.Hold();
|
||||
this._userVerifier.begin(this._userName, batch);
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||
this.emit('loaded');
|
||||
return false;
|
||||
}));
|
||||
|
||||
this._idleMonitor = Shell.IdleMonitor.get();
|
||||
// this dialog is only created after user activity (curtain drag or
|
||||
// escape key press), so the timeout will fire after IDLE_TIMEOUT seconds of inactivity
|
||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
this._idleWatchId = this._idleMonitor.add_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
|
||||
},
|
||||
|
||||
_updateOkButton: function(sensitive) {
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._promptEntry.reactive = sensitive;
|
||||
this._promptEntry.clutter_text.editable = sensitive;
|
||||
this._okButton.button.reactive = sensitive;
|
||||
this._okButton.button.can_focus = sensitive;
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this._userVerifier.clear();
|
||||
this._userVerifier.begin(this._userName, new Batch.Hold());
|
||||
_showMessage: function(userVerifier, message, styleClass) {
|
||||
if (message) {
|
||||
this._promptMessage.text = message;
|
||||
this._promptMessage.styleClass = styleClass;
|
||||
GdmUtil.fadeInActor(this._promptMessage);
|
||||
} else {
|
||||
GdmUtil.fadeOutActor(this._promptMessage);
|
||||
}
|
||||
},
|
||||
|
||||
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
if (this._firstQuestion && this._firstQuestionAnswer) {
|
||||
this._userVerifier.answerQuery(serviceName, this._firstQuestionAnswer);
|
||||
this._firstQuestionAnswer = null;
|
||||
this._firstQuestion = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._promptLabel.text = question;
|
||||
|
||||
this._promptEntry.text = '';
|
||||
if (!this._firstQuestion)
|
||||
this._promptEntry.text = '';
|
||||
else
|
||||
this._firstQuestion = false;
|
||||
|
||||
this._promptEntry.clutter_text.set_password_char(passwordChar);
|
||||
this._promptEntry.menu.isPassword = passwordChar != '';
|
||||
|
||||
this._currentQuery = serviceName;
|
||||
this._updateOkButton(true);
|
||||
this._updateSensitivity(true);
|
||||
},
|
||||
|
||||
_showLoginHint: function(verifier, message) {
|
||||
@ -191,13 +249,22 @@ const UnlockDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_doUnlock: function() {
|
||||
if (this._firstQuestion) {
|
||||
// we haven't received a query yet, so stash the answer
|
||||
// and make ourself non-reactive
|
||||
// the actual reply to GDM will be sent as soon as asked
|
||||
this._firstQuestionAnswer = this._promptEntry.text;
|
||||
this._updateSensitivity(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._currentQuery)
|
||||
return;
|
||||
|
||||
let query = this._currentQuery;
|
||||
this._currentQuery = null;
|
||||
|
||||
this._updateOkButton(false);
|
||||
this._updateSensitivity(false);
|
||||
|
||||
this._userVerifier.answerQuery(query, this._promptEntry.text);
|
||||
},
|
||||
@ -207,13 +274,24 @@ const UnlockDialog = new Lang.Class({
|
||||
this.emit('unlocked');
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this._userVerifier.cancel();
|
||||
_onReset: function() {
|
||||
this.emit('failed');
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this._currentQuery = null;
|
||||
this._firstQuestion = true;
|
||||
|
||||
this._promptEntry.text = '';
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
this._promptEntry.menu.isPassword = true;
|
||||
|
||||
this._updateSensitivity(false);
|
||||
},
|
||||
|
||||
_escape: function() {
|
||||
this._onVerificationFailed();
|
||||
this._userVerifier.cancel();
|
||||
this.emit('failed');
|
||||
},
|
||||
|
||||
_otherUserClicked: function(button, event) {
|
||||
|
@ -9,10 +9,11 @@ const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -25,6 +26,7 @@ const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
||||
|
||||
const DIALOG_ICON_SIZE = 64;
|
||||
|
||||
@ -58,6 +60,11 @@ const UserAvatarWidget = new Lang.Class({
|
||||
reactive: params.reactive });
|
||||
},
|
||||
|
||||
setSensitive: function(sensitive) {
|
||||
this.actor.can_focus = sensitive;
|
||||
this.actor.reactive = sensitive;
|
||||
},
|
||||
|
||||
update: function() {
|
||||
let iconFile = this._user.get_icon_file();
|
||||
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
@ -181,7 +188,7 @@ const IMStatusChooserItem = new Lang.Class({
|
||||
item = new IMStatusItem(_("Idle"), 'user-idle-symbolic');
|
||||
this._combo.addMenuItem(item, IMStatus.IDLE);
|
||||
|
||||
item = new IMStatusItem(_("Unavailable"), 'user-offline-symbolic');
|
||||
item = new IMStatusItem(_("Offline"), 'user-offline-symbolic');
|
||||
this._combo.addMenuItem(item, IMStatus.OFFLINE);
|
||||
|
||||
this._combo.connect('active-item-changed',
|
||||
@ -236,6 +243,10 @@ const IMStatusChooserItem = new Lang.Class({
|
||||
if (this.actor.mapped)
|
||||
this._updateUser();
|
||||
}));
|
||||
|
||||
this.connect('sensitive-changed', function(sensitive) {
|
||||
this._avatar.setSensitive(sensitive);
|
||||
});
|
||||
},
|
||||
|
||||
_restorePresence: function() {
|
||||
@ -472,10 +483,11 @@ const UserMenuButton = new Lang.Class({
|
||||
this._presence = new GnomeSession.Presence();
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._haveShutdown = true;
|
||||
this._haveSuspend = true;
|
||||
|
||||
this._accountMgr = Tp.AccountManager.dup();
|
||||
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._iconBox = new St.Bin();
|
||||
@ -496,6 +508,8 @@ const UserMenuButton = new Lang.Class({
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
this._lockedIcon = new St.Icon({ icon_name: 'changes-prevent-symbolic',
|
||||
style_class: 'popup-menu-icon' });
|
||||
|
||||
this._accountMgr.connect('most-available-presence-changed',
|
||||
Lang.bind(this, this._updatePresenceIcon));
|
||||
@ -553,17 +567,28 @@ const UserMenuButton = new Lang.Class({
|
||||
// the lockdown setting changes, which should be close enough.
|
||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
||||
function(menu, open) {
|
||||
if (open)
|
||||
this._updateHaveShutdown();
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
this._updateHaveShutdown();
|
||||
this._updateHaveSuspend();
|
||||
}));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
setLockedState: function(locked) {
|
||||
this.actor.visible = !locked;
|
||||
_sessionUpdated: function() {
|
||||
this.actor.visible = !Main.sessionMode.isGreeter;
|
||||
|
||||
let allowSettings = Main.sessionMode.allowSettings;
|
||||
this._statusChooser.setSensitive(allowSettings);
|
||||
this._systemSettings.visible = allowSettings;
|
||||
|
||||
this.setSensitive(!Main.sessionMode.isLocked);
|
||||
this._updatePresenceIcon();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
@ -586,19 +611,19 @@ const UserMenuButton = new Lang.Class({
|
||||
_updateSwitchUser: function() {
|
||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
|
||||
let multiSession = Gdm.get_session_ids().length > 1;
|
||||
|
||||
this._loginScreenItem.label.set_text(multiUser ? _("Switch User")
|
||||
: _("Switch Session"));
|
||||
this._loginScreenItem.actor.visible = allowSwitch && (multiUser || multiSession);
|
||||
this._loginScreenItem.actor.visible = allowSwitch && multiUser;
|
||||
},
|
||||
|
||||
_updateLogout: function() {
|
||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
|
||||
let systemAccount = this._user.system_account;
|
||||
let localAccount = this._user.local_account;
|
||||
let multiUser = this._userManager.has_multiple_users;
|
||||
let multiSession = Gdm.get_session_ids().length > 1;
|
||||
|
||||
this._logoutItem.actor.visible = allowLogout && (multiUser || multiSession);
|
||||
this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
|
||||
},
|
||||
|
||||
_updateLockScreen: function() {
|
||||
@ -622,9 +647,15 @@ const UserMenuButton = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSuspendOrPowerOff: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
_updateHaveSuspend: function() {
|
||||
this._loginManager.canSuspend(Lang.bind(this,
|
||||
function(result) {
|
||||
this._haveSuspend = result;
|
||||
this._updateSuspendOrPowerOff();
|
||||
}));
|
||||
},
|
||||
|
||||
_updateSuspendOrPowerOff: function() {
|
||||
if (!this._suspendOrPowerOffItem)
|
||||
return;
|
||||
|
||||
@ -647,7 +678,9 @@ const UserMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_updatePresenceIcon: function(accountMgr, presence, status, message) {
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||
if (Main.sessionMode.isLocked)
|
||||
this._iconBox.child = this._lockedIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.AVAILABLE)
|
||||
this._iconBox.child = this._availableIcon;
|
||||
else if (presence == Tp.ConnectionPresenceType.BUSY)
|
||||
this._iconBox.child = this._busyIcon;
|
||||
@ -707,8 +740,7 @@ const UserMenuButton = new Lang.Class({
|
||||
let item;
|
||||
|
||||
item = new IMStatusChooserItem();
|
||||
if (Main.sessionMode.allowSettings)
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._statusChooser = item;
|
||||
|
||||
@ -720,11 +752,10 @@ const UserMenuButton = new Lang.Class({
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
if (Main.sessionMode.allowSettings) {
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
}
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._systemSettings = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
@ -792,14 +823,15 @@ const UserMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
Main.screenShield.lock(true);
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY))
|
||||
Main.screenShield.lock(false);
|
||||
Main.screenShield.lock(false);
|
||||
Gdm.goto_login_session_sync(null);
|
||||
},
|
||||
|
||||
@ -826,12 +858,13 @@ const UserMenuButton = new Lang.Class({
|
||||
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
||||
Main.screenShield.disconnect(tmpId);
|
||||
|
||||
this._upClient.suspend_sync(null);
|
||||
this._loginManager.suspend();
|
||||
}));
|
||||
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.screenShield.lock(true);
|
||||
} else {
|
||||
this._upClient.suspend_sync(null);
|
||||
this._loginManager.suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -11,7 +12,6 @@ const St = imports.gi.St;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const RemoteSearch = imports.ui.remoteSearch;
|
||||
const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
@ -20,6 +20,7 @@ const Tweener = imports.ui.tweener;
|
||||
const Wanda = imports.ui.wanda;
|
||||
const WorkspacesView = imports.ui.workspacesView;
|
||||
|
||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
|
||||
const FocusTrap = new Lang.Class({
|
||||
Name: 'FocusTrap',
|
||||
@ -51,7 +52,7 @@ const ViewSelector = new Lang.Class({
|
||||
|
||||
this._activePage = null;
|
||||
|
||||
this.active = false;
|
||||
this.entryNonEmpty = false;
|
||||
this._searchPending = false;
|
||||
this._searchTimeoutId = 0;
|
||||
|
||||
@ -89,7 +90,7 @@ const ViewSelector = new Lang.Class({
|
||||
this._appsPage = this._addPage(this._appDisplay.actor, null,
|
||||
_("Applications"), 'system-run-symbolic');
|
||||
|
||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
|
||||
this._searchResults = new SearchDisplay.SearchDisplay(this._searchSystem);
|
||||
this._searchPage = this._addPage(this._searchResults.actor, this._entry,
|
||||
_("Search"), 'edit-find-symbolic');
|
||||
|
||||
@ -98,7 +99,6 @@ const ViewSelector = new Lang.Class({
|
||||
this.addSearchProvider(new Wanda.WandaSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
|
||||
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||
|
||||
// Load remote search providers provided by applications
|
||||
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
|
||||
@ -114,9 +114,6 @@ const ViewSelector = new Lang.Class({
|
||||
|
||||
global.focus_manager.add_group(this._searchResults.actor);
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._resetShowAppsButton));
|
||||
|
||||
this._stageKeyPressId = 0;
|
||||
Main.overview.connect('showing', Lang.bind(this,
|
||||
function () {
|
||||
@ -133,16 +130,15 @@ const ViewSelector = new Lang.Class({
|
||||
}
|
||||
}));
|
||||
|
||||
// Public constraints which may be used to tie actors' height or
|
||||
// vertical position to the current tab's content; as the content's
|
||||
// height and position depend on the view selector's style properties
|
||||
// (e.g. font size, padding, spacing, ...) it would be extremely hard
|
||||
// and ugly to get these from the outside. While it would be possible
|
||||
// to use position and height properties directly, outside code would
|
||||
// need to ensure that the content is properly allocated before
|
||||
// accessing the properties.
|
||||
this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea,
|
||||
coordinate: Clutter.BindCoordinate.HEIGHT });
|
||||
global.display.add_keybinding('toggle-application-view',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Lang.bind(this, this._showWithAppsPage));
|
||||
},
|
||||
|
||||
_showWithAppsPage: function() {
|
||||
Main.overview.show();
|
||||
this._showAppsButton.set_checked(true);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
@ -220,7 +216,9 @@ const ViewSelector = new Lang.Class({
|
||||
},
|
||||
|
||||
_onShowAppsButtonToggled: function() {
|
||||
if (this.active)
|
||||
this.emit('apps-button-checked', this._showAppsButton.checked);
|
||||
|
||||
if (this.entryNonEmpty)
|
||||
this.reset();
|
||||
else
|
||||
this._showPage(this._showAppsButton.checked ? this._appsPage
|
||||
@ -236,7 +234,7 @@ const ViewSelector = new Lang.Class({
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
if (symbol == Clutter.Escape) {
|
||||
if (this.active)
|
||||
if (this.entryNonEmpty)
|
||||
this.reset();
|
||||
else if (this._showAppsButton.checked)
|
||||
this._resetShowAppsButton();
|
||||
@ -244,9 +242,9 @@ const ViewSelector = new Lang.Class({
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
} else if (Clutter.keysym_to_unicode(symbol) ||
|
||||
(symbol == Clutter.BackSpace && this.active)) {
|
||||
(symbol == Clutter.BackSpace && this.entryNonEmpty)) {
|
||||
this.startSearch(event);
|
||||
} else if (!this.active) {
|
||||
} else if (!this.entryNonEmpty) {
|
||||
if (symbol == Clutter.Tab || symbol == Clutter.Down) {
|
||||
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
return true;
|
||||
@ -259,6 +257,7 @@ const ViewSelector = new Lang.Class({
|
||||
},
|
||||
|
||||
_searchCancelled: function() {
|
||||
this.emit('search-cancelled');
|
||||
this._showPage(this._showAppsButton.checked ? this._appsPage
|
||||
: this._workspacesPage);
|
||||
|
||||
@ -311,7 +310,7 @@ const ViewSelector = new Lang.Class({
|
||||
|
||||
startSearch: function(event) {
|
||||
global.stage.set_key_focus(this._text);
|
||||
this._text.event(event, false);
|
||||
this._text.event(event, true);
|
||||
},
|
||||
|
||||
// the entry does not show the hint
|
||||
@ -320,13 +319,14 @@ const ViewSelector = new Lang.Class({
|
||||
},
|
||||
|
||||
_onTextChanged: function (se, prop) {
|
||||
let searchPreviouslyActive = this.active;
|
||||
this.active = this._entry.get_text() != '';
|
||||
this._searchPending = this.active && !searchPreviouslyActive;
|
||||
let searchPreviouslyActive = this.entryNonEmpty;
|
||||
this.entryNonEmpty = this._entry.get_text() != '';
|
||||
this._searchPending = this.entryNonEmpty && !searchPreviouslyActive;
|
||||
if (this._searchPending) {
|
||||
this._searchResults.startingSearch();
|
||||
}
|
||||
if (this.active) {
|
||||
if (this.entryNonEmpty) {
|
||||
this.emit('search-begin');
|
||||
this._entry.set_secondary_icon(this._activeIcon);
|
||||
|
||||
if (this._iconClickedId == 0) {
|
||||
@ -343,7 +343,7 @@ const ViewSelector = new Lang.Class({
|
||||
this._entry.set_secondary_icon(this._inactiveIcon);
|
||||
this._searchCancelled();
|
||||
}
|
||||
if (!this.active) {
|
||||
if (!this.entryNonEmpty) {
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._searchTimeoutId = 0;
|
||||
@ -371,7 +371,7 @@ const ViewSelector = new Lang.Class({
|
||||
}
|
||||
this._searchResults.activateDefault();
|
||||
return true;
|
||||
} else if (this.active) {
|
||||
} else if (this.entryNonEmpty) {
|
||||
let arrowNext, nextDirection;
|
||||
if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
arrowNext = Clutter.Left;
|
||||
|
@ -1,11 +1,9 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
@ -50,21 +48,12 @@ const WandaIcon = new Lang.Class({
|
||||
},
|
||||
|
||||
createIcon: function(iconSize) {
|
||||
if (this._animations)
|
||||
this._animations.destroy();
|
||||
|
||||
if (!this._imageFile) {
|
||||
return new St.Icon({ icon_name: 'face-smile',
|
||||
icon_size: iconSize });
|
||||
}
|
||||
|
||||
this._animations = St.TextureCache.get_default().load_sliced_image(this._imageFile, this._imgWidth, this._imgHeight);
|
||||
this._animations.connect('destroy', Lang.bind(this, function() {
|
||||
if (this._timeoutId)
|
||||
GLib.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
this._animations = null;
|
||||
}));
|
||||
this._animations.connect('notify::mapped', Lang.bind(this, function() {
|
||||
if (this._animations.mapped && !this._timeoutId) {
|
||||
this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, FISH_SPEED, Lang.bind(this, this._update));
|
||||
@ -77,11 +66,16 @@ const WandaIcon = new Lang.Class({
|
||||
}
|
||||
}));
|
||||
|
||||
this._i = 0;
|
||||
|
||||
return this._animations;
|
||||
},
|
||||
|
||||
_createIconTexture: function(size) {
|
||||
if (size == this.iconSize)
|
||||
return;
|
||||
|
||||
this.parent(size);
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
let n = this._animations.get_n_children();
|
||||
if (n == 0) {
|
||||
@ -173,15 +167,8 @@ const WandaSearchProvider = new Lang.Class({
|
||||
// only one which speaks the truth!
|
||||
'name': capitalize(fish[0]),
|
||||
'createIcon': function(iconSize) {
|
||||
// for DND only (maybe could be improved)
|
||||
// DON'T use St.Icon here, it crashes the shell
|
||||
// (dnd.js code assumes it can query the actor size
|
||||
// without parenting it, while StWidget accesses
|
||||
// StThemeNode in get_preferred_width/height, which
|
||||
// triggers an assertion failure)
|
||||
return St.TextureCache.get_default().load_icon_name(null,
|
||||
'face-smile',
|
||||
iconSize);
|
||||
return new St.Icon({ gicon: Gio.icon_new_for_string('face-smile'),
|
||||
icon_size: iconSize });
|
||||
}
|
||||
}]);
|
||||
},
|
||||
|
@ -33,21 +33,13 @@ const DRAGGING_WINDOW_OPACITY = 100;
|
||||
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const BUTTON_LAYOUT_KEY = 'button-layout';
|
||||
|
||||
// Define a layout scheme for small window counts. For larger
|
||||
// counts we fall back to an algorithm. We need more schemes here
|
||||
// unless we have a really good algorithm.
|
||||
|
||||
// Each triplet is [xCenter, yCenter, scale] where the scale
|
||||
// is relative to the width of the workspace.
|
||||
const POSITIONS = {
|
||||
1: [[0.5, 0.5, 0.95]],
|
||||
2: [[0.25, 0.5, 0.48], [0.75, 0.5, 0.48]],
|
||||
3: [[0.25, 0.25, 0.48], [0.75, 0.25, 0.48], [0.5, 0.75, 0.48]],
|
||||
4: [[0.25, 0.25, 0.47], [0.75, 0.25, 0.47], [0.75, 0.75, 0.47], [0.25, 0.75, 0.47]],
|
||||
5: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.25, 0.75, 0.32], [0.75, 0.75, 0.32]]
|
||||
};
|
||||
// Used in _orderWindowsPermutations, 5! = 120 which is probably the highest we can go
|
||||
const POSITIONING_PERMUTATIONS_MAX = 5;
|
||||
// When calculating a layout, we calculate the scale of windows and the percent
|
||||
// of the available area the new layout uses. If the values for the new layout,
|
||||
// when weighted with the values as below, are worse than the previous layout's,
|
||||
// we stop looking for a new layout and use the previous layout.
|
||||
// Otherwise, we keep looking for a new layout.
|
||||
const LAYOUT_SCALE_WEIGHT = 1;
|
||||
const LAYOUT_SPACE_WEIGHT = 0.1;
|
||||
|
||||
function _interpolate(start, end, step) {
|
||||
return start + (end - start) * step;
|
||||
@ -167,6 +159,24 @@ const WindowClone = new Lang.Class({
|
||||
this._selected = false;
|
||||
},
|
||||
|
||||
get slot() {
|
||||
let x, y, w, h;
|
||||
|
||||
if (this.inDrag) {
|
||||
x = this.dragOrigX;
|
||||
y = this.dragOrigY;
|
||||
w = this.actor.width * this.dragOrigScale;
|
||||
h = this.actor.height * this.dragOrigScale;
|
||||
} else {
|
||||
x = this.actor.x;
|
||||
y = this.actor.y;
|
||||
w = this.actor.width * this.actor.scale_x;
|
||||
h = this.actor.height * this.actor.scale_y;
|
||||
}
|
||||
|
||||
return [x, y, w, h];
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
this._stackAbove = actor;
|
||||
if (this.inDrag || this._zooming)
|
||||
@ -443,6 +453,11 @@ const WindowOverlay = new Lang.Class({
|
||||
this._updateCaptionId = metaWindow.connect('notify::title',
|
||||
Lang.bind(this, function(w) {
|
||||
this.title.text = w.title;
|
||||
// we need this for the next call to get_preferred_width
|
||||
// to return useful results
|
||||
this.title.set_size(-1, -1);
|
||||
|
||||
this._repositionSelf();
|
||||
}));
|
||||
|
||||
let button = new St.Button({ style_class: 'window-close' });
|
||||
@ -504,15 +519,16 @@ const WindowOverlay = new Lang.Class({
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
chromeWidth: function () {
|
||||
return this.closeButton.width - this.closeButton._overlap;
|
||||
},
|
||||
|
||||
chromeHeights: function () {
|
||||
return [this.closeButton.height - this.closeButton._overlap,
|
||||
this.title.height + this.title._spacing];
|
||||
},
|
||||
|
||||
_repositionSelf: function() {
|
||||
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
|
||||
this.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* @cloneX: x position of windowClone
|
||||
* @cloneY: y position of windowClone
|
||||
@ -550,9 +566,8 @@ const WindowOverlay = new Lang.Class({
|
||||
else
|
||||
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
|
||||
|
||||
if (!title.fullWidth)
|
||||
title.fullWidth = title.width;
|
||||
let titleWidth = Math.min(title.fullWidth, cloneWidth);
|
||||
let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1);
|
||||
let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth));
|
||||
|
||||
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
|
||||
let titleY = cloneY + cloneHeight + title._spacing;
|
||||
@ -668,6 +683,280 @@ const WindowPositionFlags = {
|
||||
ANIMATE: 1 << 1
|
||||
};
|
||||
|
||||
const LayoutStrategy = new Lang.Class({
|
||||
Name: 'LayoutStrategy',
|
||||
Abstract: true,
|
||||
|
||||
_init: function(monitor, rowSpacing, columnSpacing, bottomPadding) {
|
||||
this._monitor = monitor;
|
||||
this._rowSpacing = rowSpacing;
|
||||
this._columnSpacing = columnSpacing;
|
||||
this._bottomPadding = bottomPadding;
|
||||
},
|
||||
|
||||
_newRow: function() {
|
||||
// Row properties:
|
||||
//
|
||||
// * x, y are the position of row, relative to area
|
||||
//
|
||||
// * width, height are the scaled versions of fullWidth, fullHeight
|
||||
//
|
||||
// * width also has the spacing in between windows. It's not in
|
||||
// fullWidth, as the spacing is constant, whereas fullWidth is
|
||||
// meant to be scaled
|
||||
//
|
||||
// * neither height/fullHeight have any sort of spacing or padding
|
||||
//
|
||||
// * if cellWidth is present, all windows in the row will occupy
|
||||
// the space of cellWidth, centered.
|
||||
return { x: 0, y: 0,
|
||||
width: 0, height: 0,
|
||||
fullWidth: 0, fullHeight: 0,
|
||||
cellWidth: 0,
|
||||
windows: [] };
|
||||
},
|
||||
|
||||
// Compute the size and fancy scale for @window using the
|
||||
// base scale, @scale.
|
||||
//
|
||||
// Returns a list structure: [ scaledWidth, scaledHeight, fancyScale ]
|
||||
// where scaledWidth and scaledHeight are the window's
|
||||
// width and height, scaled by fancyScale for convenience.
|
||||
_computeWindowSizeAndScale: function(window, scale) {
|
||||
let width = window.actor.width;
|
||||
let height = window.actor.height;
|
||||
let ratio;
|
||||
|
||||
if (width > height)
|
||||
ratio = width / this._monitor.width;
|
||||
else
|
||||
ratio = height / this._monitor.height;
|
||||
|
||||
let fancyScale = (2 / (1 + ratio)) * scale;
|
||||
return [width * fancyScale, height * fancyScale, fancyScale];
|
||||
},
|
||||
|
||||
// Compute the size of each row, by assigning to the properties
|
||||
// row.width, row.height, row.fullWidth, row.fullHeight, and
|
||||
// (optionally) row.cellWidth, for each row in @layout.rows.
|
||||
// This method is intended to be called by subclasses.
|
||||
_computeRowSizes: function(layout) {
|
||||
throw new Error('_computeRowSizes not implemented');
|
||||
},
|
||||
|
||||
// Compute strategy-specific window slots for each window in
|
||||
// @windows, given the @layout. The strategy may also use @layout
|
||||
// as strategy-specific storage.
|
||||
//
|
||||
// This must calculate:
|
||||
// * maxColumns - The maximum number of columns used by the layout.
|
||||
// * gridWidth - The total width used by the grid, unscaled, unspaced.
|
||||
// * gridHeight - The totial height used by the grid, unscaled, unspaced.
|
||||
// * rows - A list of rows, which should be instantiated by _newRow.
|
||||
computeLayout: function(windows, layout) {
|
||||
throw new Error('computeLayout not implemented');
|
||||
},
|
||||
|
||||
// Given @layout, compute the overall scale and space of the layout.
|
||||
// The scale is the individual, non-fancy scale of each window, and
|
||||
// the space is the percentage of the available area eventually
|
||||
// used by the layout.
|
||||
|
||||
// This method does not return anything, but instead installs
|
||||
// the properties "scale" and "space" on @layout directly.
|
||||
//
|
||||
// Make sure to call this methods before calling computeWindowSlots(),
|
||||
// as it depends on the scale property installed in @layout here.
|
||||
computeScaleAndSpace: function(layout) {
|
||||
let area = layout.area;
|
||||
|
||||
let hspacing = (layout.maxColumns - 1) * this._columnSpacing;
|
||||
let vspacing = (layout.numRows - 1) * this._rowSpacing + this._bottomPadding;
|
||||
|
||||
let spacedWidth = area.width - hspacing;
|
||||
let spacedHeight = area.height - vspacing;
|
||||
|
||||
let horizontalScale = spacedWidth / layout.gridWidth;
|
||||
let verticalScale = spacedHeight / layout.gridHeight;
|
||||
|
||||
// Thumbnails should be less than 70% of the original size
|
||||
let scale = Math.min(horizontalScale, verticalScale, WINDOW_CLONE_MAXIMUM_SCALE);
|
||||
|
||||
let scaledLayoutWidth = layout.gridWidth * scale + hspacing;
|
||||
let scaledLayoutHeight = layout.gridHeight * scale + vspacing;
|
||||
let space = (scaledLayoutWidth * scaledLayoutHeight) / (area.width * area.height);
|
||||
|
||||
layout.scale = scale;
|
||||
layout.space = space;
|
||||
},
|
||||
|
||||
computeWindowSlots: function(layout, area) {
|
||||
this._computeRowSizes(layout);
|
||||
|
||||
let { rows: rows, scale: scale, state: state } = layout;
|
||||
|
||||
let slots = [];
|
||||
|
||||
let y = 0;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
row.x = area.x + (area.width - row.width) / 2;
|
||||
row.y = area.y + y;
|
||||
y += row.height + this._rowSpacing;
|
||||
}
|
||||
|
||||
let height = y - this._rowSpacing + this._bottomPadding;
|
||||
let baseY = (area.height - height) / 2;
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
row.y += baseY;
|
||||
let baseX = row.x;
|
||||
for (let j = 0; j < row.windows.length; j++) {
|
||||
let window = row.windows[j];
|
||||
|
||||
let [width, height, s] = this._computeWindowSizeAndScale(window, scale);
|
||||
let y = row.y + row.height - height;
|
||||
|
||||
let x = baseX;
|
||||
if (row.cellWidth) {
|
||||
x += (row.cellWidth - width) / 2;
|
||||
width = row.cellWidth;
|
||||
}
|
||||
|
||||
slots.push([x, y, s]);
|
||||
baseX += width + this._columnSpacing;
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
});
|
||||
|
||||
const UnalignedLayoutStrategy = new Lang.Class({
|
||||
Name: 'UnalignedLayoutStrategy',
|
||||
Extends: LayoutStrategy,
|
||||
|
||||
_computeRowSizes: function(layout) {
|
||||
let { rows: rows, scale: scale } = layout;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
row.width = row.fullWidth * scale + (row.windows.length - 1) * this._columnSpacing;
|
||||
row.height = row.fullHeight * scale;
|
||||
}
|
||||
},
|
||||
|
||||
_keepSameRow: function(row, window, width, idealRowWidth) {
|
||||
if (row.fullWidth + width <= idealRowWidth)
|
||||
return true;
|
||||
|
||||
let oldRatio = row.fullWidth / idealRowWidth;
|
||||
let newRatio = (row.fullWidth + width) / idealRowWidth;
|
||||
|
||||
if (Math.abs(1 - newRatio) < Math.abs(1 - oldRatio))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
computeLayout: function(windows, layout) {
|
||||
let numRows = layout.numRows;
|
||||
|
||||
let rows = [];
|
||||
let totalWidth = 0;
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
totalWidth += windows[i].actor.width;
|
||||
}
|
||||
|
||||
let idealRowWidth = totalWidth / numRows;
|
||||
let windowIdx = 0;
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
let col = 0;
|
||||
let row = this._newRow();
|
||||
rows.push(row);
|
||||
|
||||
for (; windowIdx < windows.length; windowIdx++) {
|
||||
let window = windows[windowIdx];
|
||||
let [width, height] = this._computeWindowSizeAndScale(window, 1);
|
||||
row.fullHeight = Math.max(row.fullHeight, height);
|
||||
|
||||
// either new width is < idealWidth or new width is nearer from idealWidth then oldWidth
|
||||
if (this._keepSameRow(row, window, width, idealRowWidth) || (i == numRows - 1)) {
|
||||
row.windows.push(window);
|
||||
row.fullWidth += width;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let gridHeight = 0;
|
||||
let maxRow;
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
let row = rows[i];
|
||||
if (!maxRow || row.fullWidth > maxRow.fullWidth)
|
||||
maxRow = row;
|
||||
gridHeight += row.fullHeight;
|
||||
}
|
||||
|
||||
layout.rows = rows;
|
||||
layout.maxColumns = maxRow.windows.length;
|
||||
layout.gridWidth = maxRow.fullWidth;
|
||||
layout.gridHeight = gridHeight;
|
||||
}
|
||||
});
|
||||
|
||||
const GridLayoutStrategy = new Lang.Class({
|
||||
Name: 'GridLayoutStrategy',
|
||||
Extends: LayoutStrategy,
|
||||
|
||||
_computeRowSizes: function(layout) {
|
||||
let { rows: rows, scale: scale } = layout;
|
||||
|
||||
let gridWidth = layout.numColumns * layout.maxWindowWidth;
|
||||
let hspacing = (layout.numColumns - 1) * this._columnSpacing;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
row.fullWidth = layout.gridWidth;
|
||||
row.fullHeight = layout.maxWindowHeight;
|
||||
|
||||
row.width = row.fullWidth * scale + hspacing;
|
||||
row.height = row.fullHeight * scale;
|
||||
row.cellWidth = layout.maxWindowWidth * scale;
|
||||
}
|
||||
},
|
||||
|
||||
computeLayout: function(windows, layout) {
|
||||
let { numRows: numRows, numColumns: numColumns } = layout;
|
||||
let rows = [];
|
||||
let windowIdx = 0;
|
||||
|
||||
let maxWindowWidth = 0;
|
||||
let maxWindowHeight = 0;
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
let row = this._newRow();
|
||||
rows.push(row);
|
||||
for (; windowIdx < windows.length; windowIdx++) {
|
||||
if (row.windows.length >= numColumns)
|
||||
break;
|
||||
|
||||
let window = windows[windowIdx];
|
||||
row.windows.push(window);
|
||||
|
||||
let [width, height] = this._computeWindowSizeAndScale(window, 1);
|
||||
maxWindowWidth = Math.max(maxWindowWidth, width);
|
||||
maxWindowHeight = Math.max(maxWindowHeight, height);
|
||||
}
|
||||
}
|
||||
|
||||
layout.rows = rows;
|
||||
layout.maxColumns = numColumns;
|
||||
layout.gridWidth = numColumns * maxWindowWidth;
|
||||
layout.gridHeight = numRows * maxWindowHeight;
|
||||
layout.maxWindowWidth = maxWindowWidth;
|
||||
layout.maxWindowHeight = maxWindowHeight;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @metaWorkspace: a #Meta.Workspace, or null
|
||||
*/
|
||||
@ -689,7 +978,7 @@ const Workspace = new Lang.Class({
|
||||
// Without this the drop area will be overlapped.
|
||||
this._windowOverlaysGroup.set_size(0, 0);
|
||||
|
||||
this.actor = new Clutter.Group();
|
||||
this.actor = new St.Widget({ style_class: 'window-picker' });
|
||||
this.actor.set_size(0, 0);
|
||||
|
||||
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
|
||||
@ -729,6 +1018,8 @@ const Workspace = new Lang.Class({
|
||||
|
||||
this._positionWindowsFlags = 0;
|
||||
this._positionWindowsId = 0;
|
||||
|
||||
this._currentLayout = null;
|
||||
},
|
||||
|
||||
setGeometry: function(x, y, width, height) {
|
||||
@ -763,225 +1054,6 @@ const Workspace = new Lang.Class({
|
||||
return this._windows.length == 0;
|
||||
},
|
||||
|
||||
// Only use this for n <= 20 say
|
||||
_factorial: function(n) {
|
||||
let result = 1;
|
||||
for (let i = 2; i <= n; i++)
|
||||
result *= i;
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* _permutation:
|
||||
* @permutationIndex: An integer from [0, list.length!)
|
||||
* @list: (inout): Array of objects to permute; will be modified in place
|
||||
*
|
||||
* Given an integer between 0 and length of array, re-order the array in-place
|
||||
* into a permutation denoted by the index.
|
||||
*/
|
||||
_permutation: function(permutationIndex, list) {
|
||||
for (let j = 2; j <= list.length; j++) {
|
||||
let firstIndex = (permutationIndex % j);
|
||||
let secondIndex = j - 1;
|
||||
// Swap
|
||||
let tmp = list[firstIndex];
|
||||
list[firstIndex] = list[secondIndex];
|
||||
list[secondIndex] = tmp;
|
||||
permutationIndex = Math.floor(permutationIndex / j);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* _forEachPermutations:
|
||||
* @list: Array
|
||||
* @func: Function which takes a single array argument
|
||||
*
|
||||
* Call @func with each permutation of @list as an argument.
|
||||
*/
|
||||
_forEachPermutations: function(list, func) {
|
||||
let nCombinations = this._factorial(list.length);
|
||||
for (let i = 0; i < nCombinations; i++) {
|
||||
let listCopy = list.concat();
|
||||
this._permutation(i, listCopy);
|
||||
func(listCopy);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* _computeWindowMotion:
|
||||
* @actor: A #WindowClone's #ClutterActor
|
||||
* @slot: An element of #POSITIONS
|
||||
* @slotGeometry: Layout of @slot
|
||||
*
|
||||
* Returns a number corresponding to how much perceived motion
|
||||
* would be involved in moving the window to the given slot.
|
||||
* Currently this is the square of the distance between the
|
||||
* centers.
|
||||
*/
|
||||
_computeWindowMotion: function (actor, slot) {
|
||||
let [xCenter, yCenter, fraction] = slot;
|
||||
let xDelta, yDelta, distanceSquared;
|
||||
let actorWidth, actorHeight;
|
||||
|
||||
let x = actor.x;
|
||||
let y = actor.y;
|
||||
let scale = actor.scale_x;
|
||||
|
||||
if (actor._delegate.inDrag) {
|
||||
x = actor._delegate.dragOrigX;
|
||||
y = actor._delegate.dragOrigY;
|
||||
scale = actor._delegate.dragOrigScale;
|
||||
}
|
||||
|
||||
actorWidth = actor.width * scale;
|
||||
actorHeight = actor.height * scale;
|
||||
xDelta = x + actorWidth / 2.0 - xCenter * this._width - this._x;
|
||||
yDelta = y + actorHeight / 2.0 - yCenter * this._height - this._y;
|
||||
distanceSquared = xDelta * xDelta + yDelta * yDelta;
|
||||
|
||||
return distanceSquared;
|
||||
},
|
||||
|
||||
/**
|
||||
* _orderWindowsPermutations:
|
||||
*
|
||||
* Iterate over all permutations of the windows, and determine the
|
||||
* permutation which has the least total motion.
|
||||
*/
|
||||
_orderWindowsPermutations: function (clones, slots) {
|
||||
let minimumMotionPermutation = null;
|
||||
let minimumMotion = -1;
|
||||
let permIndex = 0;
|
||||
this._forEachPermutations(clones, Lang.bind(this, function (permutation) {
|
||||
let motion = 0;
|
||||
for (let i = 0; i < permutation.length; i++) {
|
||||
let cloneActor = permutation[i].actor;
|
||||
let slot = slots[i];
|
||||
|
||||
let delta = this._computeWindowMotion(cloneActor, slot);
|
||||
|
||||
motion += delta;
|
||||
|
||||
// Bail out early if we're already larger than the
|
||||
// previous best
|
||||
if (minimumMotionPermutation != null &&
|
||||
motion > minimumMotion)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (minimumMotionPermutation == null || motion < minimumMotion) {
|
||||
minimumMotionPermutation = permutation;
|
||||
minimumMotion = motion;
|
||||
}
|
||||
permIndex++;
|
||||
}));
|
||||
return minimumMotionPermutation;
|
||||
},
|
||||
|
||||
/**
|
||||
* _orderWindowsGreedy:
|
||||
*
|
||||
* Iterate over available slots in order, placing into each one the window
|
||||
* we find with the smallest motion to that slot.
|
||||
*/
|
||||
_orderWindowsGreedy: function(clones, slots) {
|
||||
let result = [];
|
||||
let slotIndex = 0;
|
||||
// Copy since we mutate below
|
||||
let clonesCopy = clones.concat();
|
||||
for (let i = 0; i < slots.length; i++) {
|
||||
let slot = slots[i];
|
||||
let minimumMotionIndex = -1;
|
||||
let minimumMotion = -1;
|
||||
for (let j = 0; j < clonesCopy.length; j++) {
|
||||
let cloneActor = clonesCopy[j].actor;
|
||||
let delta = this._computeWindowMotion(cloneActor, slot);
|
||||
if (minimumMotionIndex == -1 || delta < minimumMotion) {
|
||||
minimumMotionIndex = j;
|
||||
minimumMotion = delta;
|
||||
}
|
||||
}
|
||||
result.push(clonesCopy[minimumMotionIndex]);
|
||||
clonesCopy.splice(minimumMotionIndex, 1);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* _orderWindowsByMotionAndStartup:
|
||||
* @windows: Array of #MetaWindow
|
||||
* @slots: Array of slots
|
||||
*
|
||||
* Returns a copy of @windows, ordered in such a way that they require least motion
|
||||
* to move to the final screen coordinates of @slots. Ties are broken in a stable
|
||||
* fashion by the order in which the windows were created.
|
||||
*/
|
||||
_orderWindowsByMotionAndStartup: function(clones, slots) {
|
||||
clones.sort(function(w1, w2) {
|
||||
return w2.metaWindow.get_stable_sequence() - w1.metaWindow.get_stable_sequence();
|
||||
});
|
||||
if (clones.length <= POSITIONING_PERMUTATIONS_MAX)
|
||||
return this._orderWindowsPermutations(clones, slots);
|
||||
else
|
||||
return this._orderWindowsGreedy(clones, slots);
|
||||
},
|
||||
|
||||
/**
|
||||
* _getSlotGeometry:
|
||||
* @slot: A layout slot
|
||||
*
|
||||
* Returns: the screen-relative [x, y, width, height]
|
||||
* of a given window layout slot.
|
||||
*/
|
||||
_getSlotGeometry: function(slot) {
|
||||
let [xCenter, yCenter, fraction] = slot;
|
||||
|
||||
let width = this._width * fraction;
|
||||
let height = this._height * fraction;
|
||||
|
||||
let x = this._x + xCenter * this._width - width / 2 ;
|
||||
let y = this._y + yCenter * this._height - height / 2;
|
||||
|
||||
return [x, y, width, height];
|
||||
},
|
||||
|
||||
/**
|
||||
* _computeWindowLayout:
|
||||
* @metaWindow: A #MetaWindow
|
||||
* @slot: A layout slot
|
||||
*
|
||||
* Given a window and slot to fit it in, compute its
|
||||
* screen-relative [x, y, scale] where scale applies
|
||||
* to both X and Y directions.
|
||||
*/
|
||||
_computeWindowLayout: function(metaWindow, slot) {
|
||||
let [x, y, width, height] = this._getSlotGeometry(slot);
|
||||
|
||||
let rect = metaWindow.get_outer_rect();
|
||||
let buttonOuterHeight, captionHeight;
|
||||
let buttonOuterWidth = 0;
|
||||
|
||||
if (this._windowOverlays[0]) {
|
||||
[buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights();
|
||||
buttonOuterWidth = this._windowOverlays[0].chromeWidth();
|
||||
} else
|
||||
[buttonOuterHeight, captionHeight] = [0, 0];
|
||||
|
||||
let scale = Math.min((width - buttonOuterWidth) / rect.width,
|
||||
(height - buttonOuterHeight - captionHeight) / rect.height,
|
||||
WINDOW_CLONE_MAXIMUM_SCALE);
|
||||
|
||||
x = Math.floor(x + (width - scale * rect.width) / 2);
|
||||
|
||||
// We want to center the window in case we have just one
|
||||
if (metaWindow.get_workspace().n_windows == 1)
|
||||
y = Math.floor(y + (height - scale * rect.height) / 2);
|
||||
else
|
||||
y = Math.floor(y + height - rect.height * scale - captionHeight);
|
||||
|
||||
return [x, y, scale];
|
||||
},
|
||||
|
||||
setReservedSlot: function(clone) {
|
||||
if (this._reservedSlot == clone)
|
||||
return;
|
||||
@ -990,6 +1062,7 @@ const Workspace = new Lang.Class({
|
||||
clone = null;
|
||||
|
||||
this._reservedSlot = clone;
|
||||
this._currentLayout = null;
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
||||
},
|
||||
|
||||
@ -1000,6 +1073,9 @@ const Workspace = new Lang.Class({
|
||||
* ANIMATE - Indicates that we need animate changing position.
|
||||
*/
|
||||
positionWindows: function(flags) {
|
||||
if (this.leavingOverview)
|
||||
return;
|
||||
|
||||
this._positionWindowsFlags |= flags;
|
||||
|
||||
if (this._positionWindowsId > 0)
|
||||
@ -1020,6 +1096,11 @@ const Workspace = new Lang.Class({
|
||||
}
|
||||
|
||||
let clones = this._windows.slice();
|
||||
|
||||
clones.sort(function(a, b) {
|
||||
return a.metaWindow.get_stable_sequence() - b.metaWindow.get_stable_sequence();
|
||||
});
|
||||
|
||||
if (this._reservedSlot)
|
||||
clones.push(this._reservedSlot);
|
||||
|
||||
@ -1027,8 +1108,7 @@ const Workspace = new Lang.Class({
|
||||
let animate = flags & WindowPositionFlags.ANIMATE;
|
||||
|
||||
// Start the animations
|
||||
let slots = this._computeAllWindowSlots(clones.length);
|
||||
clones = this._orderWindowsByMotionAndStartup(clones, slots);
|
||||
let slots = this._computeAllWindowSlots(clones);
|
||||
|
||||
let currentWorkspace = global.screen.get_active_workspace();
|
||||
let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
|
||||
@ -1045,10 +1125,11 @@ const Workspace = new Lang.Class({
|
||||
if (clone.inDrag)
|
||||
continue;
|
||||
|
||||
let [x, y, scale] = this._computeWindowLayout(metaWindow, slot);
|
||||
let [x, y, scale] = slot;
|
||||
|
||||
if (overlay && initialPositioning)
|
||||
overlay.hide();
|
||||
|
||||
if (animate && isOnCurrentWorkspace) {
|
||||
if (!metaWindow.showing_on_its_workspace()) {
|
||||
/* Hidden windows should fade in and grow
|
||||
@ -1072,6 +1153,8 @@ const Workspace = new Lang.Class({
|
||||
|
||||
this._animateClone(clone, overlay, x, y, scale, initialPositioning);
|
||||
} else {
|
||||
// cancel any active tweens (otherwise they might override our changes)
|
||||
Tweener.removeTweens(clone.actor);
|
||||
clone.actor.set_position(x, y);
|
||||
clone.actor.set_scale(scale, scale);
|
||||
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
|
||||
@ -1113,11 +1196,11 @@ const Workspace = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateWindowOverlayPositions: function(clone, overlay, x, y, scale, animate) {
|
||||
if (!overlay)
|
||||
return;
|
||||
|
||||
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
||||
cloneWidth = scale * cloneWidth;
|
||||
cloneHeight = scale * cloneHeight;
|
||||
if (overlay)
|
||||
overlay.updatePositions(x, y, cloneWidth, cloneHeight, animate);
|
||||
overlay.updatePositions(x, y, cloneWidth * scale, cloneHeight * scale, animate);
|
||||
},
|
||||
|
||||
_showWindowOverlay: function(clone, overlay, fade) {
|
||||
@ -1211,6 +1294,7 @@ const Workspace = new Lang.Class({
|
||||
this._cursorX = x;
|
||||
this._cursorY = y;
|
||||
|
||||
this._currentLayout = null;
|
||||
this._repositionWindowsId = Mainloop.timeout_add(750,
|
||||
Lang.bind(this, this._delayedWindowRepositioning));
|
||||
},
|
||||
@ -1263,6 +1347,7 @@ const Workspace = new Lang.Class({
|
||||
clone.actor.set_position (this._x, this._y);
|
||||
}
|
||||
|
||||
this._currentLayout = null;
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
||||
},
|
||||
|
||||
@ -1300,6 +1385,8 @@ const Workspace = new Lang.Class({
|
||||
|
||||
// Animate the full-screen to Overview transition.
|
||||
zoomToOverview : function() {
|
||||
this._currentLayout = null;
|
||||
|
||||
// Position and scale the windows.
|
||||
if (Main.overview.animationInProgress)
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL);
|
||||
@ -1464,29 +1551,108 @@ const Workspace = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_computeWindowSlot : function(windowIndex, numberOfWindows) {
|
||||
if (numberOfWindows in POSITIONS)
|
||||
return POSITIONS[numberOfWindows][windowIndex];
|
||||
_isBetterLayout: function(oldLayout, newLayout) {
|
||||
if (oldLayout.scale === undefined)
|
||||
return true;
|
||||
|
||||
// If we don't have a predefined scheme for this window count,
|
||||
// arrange the windows in a grid pattern.
|
||||
let gridWidth = Math.ceil(Math.sqrt(numberOfWindows));
|
||||
let gridHeight = Math.ceil(numberOfWindows / gridWidth);
|
||||
let spacePower = (newLayout.space - oldLayout.space) * LAYOUT_SPACE_WEIGHT;
|
||||
let scalePower = (newLayout.scale - oldLayout.scale) * LAYOUT_SCALE_WEIGHT;
|
||||
|
||||
let fraction = 0.95 * (1. / gridWidth);
|
||||
|
||||
let xCenter = (.5 / gridWidth) + ((windowIndex) % gridWidth) / gridWidth;
|
||||
let yCenter = (.5 / gridHeight) + Math.floor((windowIndex / gridWidth)) / gridHeight;
|
||||
|
||||
return [xCenter, yCenter, fraction];
|
||||
if (newLayout.scale > oldLayout.scale && newLayout.space > oldLayout.space) {
|
||||
// Win win -- better scale and better space
|
||||
return true;
|
||||
} else if (newLayout.scale > oldLayout.scale && newLayout.space <= oldLayout.space) {
|
||||
// Keep new layout only if scale gain outweights aspect space loss
|
||||
return scalePower > spacePower;
|
||||
} else if (newLayout.scale <= oldLayout.scale && newLayout.space > oldLayout.space) {
|
||||
// Keep new layout only if aspect space gain outweights scale loss
|
||||
return spacePower > scalePower;
|
||||
} else {
|
||||
// Lose -- worse scale and space
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_computeAllWindowSlots: function(totalWindows) {
|
||||
let slots = [];
|
||||
for (let i = 0; i < totalWindows; i++) {
|
||||
slots.push(this._computeWindowSlot(i, totalWindows));
|
||||
_computeLayout: function(windows, area, rowSpacing, columnSpacing, bottomPadding) {
|
||||
// We look for the largest scale that allows us to fit the
|
||||
// largest row/tallest column on the workspace.
|
||||
|
||||
let lastLayout = {};
|
||||
|
||||
for (let numRows = 1; ; numRows++) {
|
||||
let numColumns = Math.ceil(windows.length / numRows);
|
||||
|
||||
// If adding a new row does not change column count just stop
|
||||
// (for instance: 9 windows, with 3 rows -> 3 columns, 4 rows ->
|
||||
// 3 columns as well => just use 3 rows then)
|
||||
if (numColumns == lastLayout.numColumns)
|
||||
break;
|
||||
|
||||
let strategyClass = numRows > 2 ? GridLayoutStrategy : UnalignedLayoutStrategy;
|
||||
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing, bottomPadding);
|
||||
|
||||
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
|
||||
strategy.computeLayout(windows, layout);
|
||||
strategy.computeScaleAndSpace(layout);
|
||||
|
||||
if (!this._isBetterLayout(lastLayout, layout))
|
||||
break;
|
||||
|
||||
lastLayout = layout;
|
||||
}
|
||||
return slots;
|
||||
|
||||
return lastLayout;
|
||||
},
|
||||
|
||||
_rectEqual: function(one, two) {
|
||||
if (one == two)
|
||||
return true;
|
||||
|
||||
return (one.x == two.x &&
|
||||
one.y == two.y &&
|
||||
one.width == two.width &&
|
||||
one.height == two.height);
|
||||
},
|
||||
|
||||
_computeAllWindowSlots: function(windows) {
|
||||
let totalWindows = windows.length;
|
||||
let node = this.actor.get_theme_node();
|
||||
|
||||
// Window grid spacing
|
||||
let columnSpacing = node.get_length('-horizontal-spacing');
|
||||
let rowSpacing = node.get_length('-vertical-spacing');
|
||||
|
||||
if (!totalWindows)
|
||||
return [];
|
||||
|
||||
let closeButtonHeight, captionHeight;
|
||||
if (this._windowOverlays.length) {
|
||||
// All of the overlays have the same chrome sizes,
|
||||
// so just pick the first one.
|
||||
let overlay = this._windowOverlays[0];
|
||||
[closeButtonHeight, captionHeight] = overlay.chromeHeights();
|
||||
} else {
|
||||
[closeButtonHeight, captionHeight] = [0, 0];
|
||||
}
|
||||
|
||||
rowSpacing += captionHeight;
|
||||
|
||||
let area = { x: this._x, y: this._y, width: this._width, height: this._height };
|
||||
area.y += closeButtonHeight;
|
||||
area.height -= closeButtonHeight;
|
||||
|
||||
if (!this._currentLayout)
|
||||
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing, captionHeight);
|
||||
|
||||
let layout = this._currentLayout;
|
||||
let strategy = layout.strategy;
|
||||
|
||||
if (!this._rectEqual(area, layout.area)) {
|
||||
layout.area = area;
|
||||
strategy.computeScaleAndSpace(layout);
|
||||
}
|
||||
|
||||
return strategy.computeWindowSlots(layout, area);
|
||||
},
|
||||
|
||||
_onCloneSelected : function (clone, time) {
|
||||
|
@ -493,6 +493,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer({ reactive: true,
|
||||
style_class: 'workspace-thumbnails',
|
||||
can_focus: true,
|
||||
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
@ -526,6 +527,8 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
|
||||
this.actor.add_actor(this._dropPlaceholder);
|
||||
|
||||
this.visible = true;
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
this._pendingScaleUpdate = false;
|
||||
@ -541,12 +544,21 @@ const ThumbnailsBox = new Lang.Class({
|
||||
|
||||
this.actor.connect('button-press-event', function() { return true; });
|
||||
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||
this.actor.connect('scroll-event',
|
||||
Lang.bind(this, this._onScrollEvent));
|
||||
this.actor.connect('key-release-event',
|
||||
Lang.bind(this, this._onKeyRelease));
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._createThumbnails));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._destroyThumbnails));
|
||||
|
||||
Main.overview.connect('app-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
Main.overview.connect('item-drag-end',
|
||||
Main.overview.connect('app-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
Main.overview.connect('item-drag-cancelled',
|
||||
Main.overview.connect('app-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
@ -620,9 +632,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
if (!source.realWindow && !source.shellWorkspaceLaunch && source != Main.xdndHandler)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
if (!Meta.prefs_get_dynamic_workspaces())
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
let canCreateWorkspaces = Meta.prefs_get_dynamic_workspaces();
|
||||
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||
|
||||
this._dropWorkspace = -1;
|
||||
@ -647,7 +657,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
if (i == this._dropPlaceholderPos)
|
||||
targetBottom += this._dropPlaceholder.get_height();
|
||||
|
||||
if (y > targetTop && y <= targetBottom && source != Main.xdndHandler) {
|
||||
if (y > targetTop && y <= targetBottom && source != Main.xdndHandler && canCreateWorkspaces) {
|
||||
placeholderPos = i;
|
||||
break;
|
||||
} else if (y > targetBottom && y <= nextTargetTop) {
|
||||
@ -721,10 +731,16 @@ const ThumbnailsBox = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
show: function() {
|
||||
_createThumbnails: function() {
|
||||
this._switchWorkspaceNotifyId =
|
||||
global.window_manager.connect('switch-workspace',
|
||||
Lang.bind(this, this._activeWorkspaceChanged));
|
||||
this._nWorkspacesNotifyId =
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._workspacesChanged));
|
||||
this._syncStackingId =
|
||||
Main.overview.connect('sync-window-stacking',
|
||||
Lang.bind(this, this.syncStacking));
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
@ -748,17 +764,86 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this.addThumbnails(0, global.screen.n_workspaces);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
_destroyThumbnails: function() {
|
||||
if (this._switchWorkspaceNotifyId > 0) {
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
this._switchWorkspaceNotifyId = 0;
|
||||
}
|
||||
if (this._nWorkspacesNotifyId > 0) {
|
||||
global.screen.disconnect(this._nWorkspacesNotifyId);
|
||||
this._nWorkspacesNotifyId = 0;
|
||||
}
|
||||
if (this._syncStackingId > 0) {
|
||||
Main.overview.disconnect(this._syncStackingId);
|
||||
this._syncStackingId = 0;
|
||||
}
|
||||
|
||||
for (let w = 0; w < this._thumbnails.length; w++)
|
||||
this._thumbnails[w].destroy();
|
||||
this._thumbnails = [];
|
||||
},
|
||||
|
||||
_computeThumbnailsX: function() {
|
||||
this._targetX = this.actor.get_x();
|
||||
|
||||
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
|
||||
|
||||
if (rtl)
|
||||
this._hiddenX = -this.actor.width;
|
||||
else
|
||||
this._hiddenX = this._targetX + this.actor.width;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (this.visible)
|
||||
return;
|
||||
|
||||
this.visible = true;
|
||||
|
||||
this.actor.show();
|
||||
Tweener.addTween(this.actor, { translation_x: this._targetX,
|
||||
transition: 'easeOutQuad',
|
||||
time: SLIDE_ANIMATION_TIME
|
||||
});
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (!this.visible)
|
||||
return;
|
||||
|
||||
this.visible = false;
|
||||
|
||||
Tweener.addTween(this.actor, { translation_x: this._hiddenX,
|
||||
transition: 'easeOutQuad',
|
||||
time: SLIDE_ANIMATION_TIME,
|
||||
onComplete: Lang.bind(this, function () {
|
||||
this.actor.hide();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_workspacesChanged: function() {
|
||||
let oldNumWorkspaces = this._thumbnails.length;
|
||||
let newNumWorkspaces = global.screen.n_workspaces;
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
|
||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||
this.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
|
||||
} else {
|
||||
let removedIndex;
|
||||
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
||||
for (let w = 0; w < oldNumWorkspaces; w++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(w);
|
||||
if (this._thumbnails[w].metaWorkspace != metaWorkspace) {
|
||||
removedIndex = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.removeThumbnails(removedIndex, removedNum);
|
||||
}
|
||||
},
|
||||
|
||||
addThumbnails: function(start, count) {
|
||||
for (let k = start; k < start + count; k++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(k);
|
||||
@ -785,7 +870,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._indicator.raise_top();
|
||||
},
|
||||
|
||||
removeThumbmails: function(start, count) {
|
||||
removeThumbnails: function(start, count) {
|
||||
let currentPos = 0;
|
||||
for (let k = 0; k < this._thumbnails.length; k++) {
|
||||
let thumbnail = this._thumbnails[k];
|
||||
@ -804,7 +889,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
syncStacking: function(actor, stackIndices) {
|
||||
for (let i = 0; i < this._thumbnails.length; i++)
|
||||
this._thumbnails[i].syncStacking(stackIndices);
|
||||
},
|
||||
@ -926,6 +1011,8 @@ const ThumbnailsBox = new Lang.Class({
|
||||
onCompleteScope: this
|
||||
});
|
||||
});
|
||||
|
||||
this._computeThumbnailsX();
|
||||
},
|
||||
|
||||
_queueUpdateStates: function() {
|
||||
@ -1159,5 +1246,30 @@ const ThumbnailsBox = new Lang.Class({
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
_onScrollEvent: function (actor, event) {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_onKeyRelease: function (actor, event) {
|
||||
switch (event.get_key_symbol()) {
|
||||
case Clutter.KEY_Up:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
break;
|
||||
case Clutter.KEY_Down:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
break;
|
||||
case Clutter.KEY_Return:
|
||||
Main.overview.toggle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -22,8 +22,6 @@ const MAX_WORKSPACES = 16;
|
||||
|
||||
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
|
||||
|
||||
const CONTROLS_POP_IN_TIME = 0.1;
|
||||
|
||||
|
||||
const WorkspacesView = new Lang.Class({
|
||||
Name: 'WorkspacesView',
|
||||
@ -51,7 +49,6 @@ const WorkspacesView = new Lang.Class({
|
||||
this._clipY = 0;
|
||||
this._clipWidth = 0;
|
||||
this._clipHeight = 0;
|
||||
this._workspaceRatioSpacing = 0;
|
||||
this._spacing = 0;
|
||||
this._animating = false; // tweening
|
||||
this._scrolling = false; // swipe-scrolling
|
||||
@ -72,6 +69,7 @@ const WorkspacesView = new Lang.Class({
|
||||
this._workspaces[w].actor.reparent(this.actor);
|
||||
this._workspaces[activeWorkspaceIndex].actor.raise_top();
|
||||
|
||||
this._extraWorkspaces = [];
|
||||
this._updateExtraWorkspaces();
|
||||
|
||||
// Position/scale the desktop windows and their children after the
|
||||
@ -83,8 +81,6 @@ const WorkspacesView = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomToOverview();
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
for (let w = 0; w < this._extraWorkspaces.length; w++)
|
||||
this._extraWorkspaces[w].zoomToOverview();
|
||||
}));
|
||||
@ -108,9 +104,9 @@ const WorkspacesView = new Lang.Class({
|
||||
global.window_manager.connect('switch-workspace',
|
||||
Lang.bind(this, this._activeWorkspaceChanged));
|
||||
|
||||
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
|
||||
this._appDragBeginId = Main.overview.connect('app-drag-begin',
|
||||
Lang.bind(this, this._dragBegin));
|
||||
this._itemDragEndId = Main.overview.connect('item-drag-end',
|
||||
this._appDragEndId = Main.overview.connect('app-drag-end',
|
||||
Lang.bind(this, this._dragEnd));
|
||||
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._dragBegin));
|
||||
@ -124,7 +120,6 @@ const WorkspacesView = new Lang.Class({
|
||||
if (!this._settings.get_boolean('workspaces-only-on-primary'))
|
||||
return;
|
||||
|
||||
this._extraWorkspaces = [];
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (i == Main.layoutManager.primaryIndex)
|
||||
@ -139,23 +134,20 @@ const WorkspacesView = new Lang.Class({
|
||||
},
|
||||
|
||||
_destroyExtraWorkspaces: function() {
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
|
||||
for (let m = 0; m < this._extraWorkspaces.length; m++)
|
||||
this._extraWorkspaces[m].destroy();
|
||||
this._extraWorkspaces = null;
|
||||
this._extraWorkspaces = [];
|
||||
},
|
||||
|
||||
setGeometry: function(x, y, width, height, spacing) {
|
||||
setGeometry: function(x, y, width, height) {
|
||||
if (this._x == x && this._y == y &&
|
||||
this._width == width && this._height == height)
|
||||
return;
|
||||
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._workspaceRatioSpacing = spacing;
|
||||
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].setGeometry(x, y, width, height);
|
||||
@ -191,8 +183,6 @@ const WorkspacesView = new Lang.Class({
|
||||
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomFromOverview();
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
for (let w = 0; w < this._extraWorkspaces.length; w++)
|
||||
this._extraWorkspaces[w].zoomFromOverview();
|
||||
},
|
||||
@ -204,8 +194,6 @@ const WorkspacesView = new Lang.Class({
|
||||
syncStacking: function(stackIndices) {
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].syncStacking(stackIndices);
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
for (let i = 0; i < this._extraWorkspaces.length; i++)
|
||||
this._extraWorkspaces[i].syncStacking(stackIndices);
|
||||
},
|
||||
@ -234,7 +222,7 @@ const WorkspacesView = new Lang.Class({
|
||||
|
||||
Tweener.removeTweens(workspace.actor);
|
||||
|
||||
let y = (w - active) * (this._height + this._spacing + this._workspaceRatioSpacing);
|
||||
let y = (w - active) * (this._height + this._spacing);
|
||||
|
||||
if (showAnimation) {
|
||||
let params = { y: y,
|
||||
@ -337,13 +325,13 @@ const WorkspacesView = new Lang.Class({
|
||||
if (this._inDrag)
|
||||
this._dragEnd();
|
||||
|
||||
if (this._itemDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._itemDragBeginId);
|
||||
this._itemDragBeginId = 0;
|
||||
if (this._appDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._appDragBeginId);
|
||||
this._appDragBeginId = 0;
|
||||
}
|
||||
if (this._itemDragEndId > 0) {
|
||||
Main.overview.disconnect(this._itemDragEndId);
|
||||
this._itemDragEndId = 0;
|
||||
if (this._appDragEndId > 0) {
|
||||
Main.overview.disconnect(this._appDragEndId);
|
||||
this._appDragEndId = 0;
|
||||
}
|
||||
if (this._windowDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._windowDragBeginId);
|
||||
@ -376,9 +364,6 @@ const WorkspacesView = new Lang.Class({
|
||||
this._firstDragMotion = false;
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
|
||||
if (!this._extraWorkspaces)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
for (let i = 0; i < this._extraWorkspaces.length; i++)
|
||||
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
|
||||
}
|
||||
@ -392,9 +377,6 @@ const WorkspacesView = new Lang.Class({
|
||||
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].setReservedSlot(null);
|
||||
|
||||
if (!this._extraWorkspaces)
|
||||
return;
|
||||
for (let i = 0; i < this._extraWorkspaces.length; i++)
|
||||
this._extraWorkspaces[i].setReservedSlot(null);
|
||||
},
|
||||
@ -476,25 +458,8 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
|
||||
this.actor.set_clip_to_allocation(true);
|
||||
|
||||
let controls = new St.Bin({ style_class: 'workspace-controls',
|
||||
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
|
||||
y_align: St.Align.START,
|
||||
y_fill: true });
|
||||
this._controls = controls;
|
||||
this.actor.add_actor(controls);
|
||||
|
||||
controls.reactive = true;
|
||||
controls.track_hover = true;
|
||||
controls.connect('notify::hover',
|
||||
Lang.bind(this, this._onControlsHoverChanged));
|
||||
controls.connect('scroll-event',
|
||||
Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
this._primaryIndex = Main.layoutManager.primaryIndex;
|
||||
|
||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||
controls.add_actor(this._thumbnailsBox.actor);
|
||||
|
||||
this._workspacesViews = null;
|
||||
this._primaryScrollAdjustment = null;
|
||||
|
||||
@ -507,77 +472,47 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._inDrag = false;
|
||||
this._cancelledDrag = false;
|
||||
|
||||
this._controlsInitiallyHovered = false;
|
||||
this._alwaysZoomOut = false;
|
||||
this._zoomOut = false;
|
||||
this._zoomFraction = 0;
|
||||
|
||||
this._updateAlwaysZoom();
|
||||
|
||||
// If we stop hiding the overview on layout changes, we will need to
|
||||
// update the _workspacesViews here
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
|
||||
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
|
||||
this._alwaysZoomOut = true;
|
||||
}));
|
||||
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, function(){
|
||||
this._alwaysZoomOut = false;
|
||||
this._updateAlwaysZoom();
|
||||
}));
|
||||
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._workspacesChanged));
|
||||
|
||||
this._switchWorkspaceNotifyId = 0;
|
||||
|
||||
this._itemDragBeginId = 0;
|
||||
this._itemDragCancelledId = 0;
|
||||
this._itemDragEndId = 0;
|
||||
this._appDragBeginId = 0;
|
||||
this._appDragCancelledId = 0;
|
||||
this._appDragEndId = 0;
|
||||
this._windowDragBeginId = 0;
|
||||
this._windowDragCancelledId = 0;
|
||||
this._windowDragEndId = 0;
|
||||
this._notifyOpacityId = 0;
|
||||
this._swipeScrollBeginId = 0;
|
||||
this._swipeScrollEndId = 0;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
|
||||
this._settings.connect('changed::dynamic-workspaces',
|
||||
Lang.bind(this, this._updateSwitcherVisibility));
|
||||
},
|
||||
|
||||
_updateSwitcherVisibility: function() {
|
||||
this._thumbnailsBox.actor.visible =
|
||||
this._settings.get_boolean('dynamic-workspaces') ||
|
||||
global.screen.n_workspaces > 1;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if(!this._alwaysZoomOut) {
|
||||
let [mouseX, mouseY] = global.get_pointer();
|
||||
let [x, y] = this._controls.get_transformed_position();
|
||||
let [width, height] = this._controls.get_transformed_size();
|
||||
let visibleWidth = this._controls.get_theme_node().get_length('visible-width');
|
||||
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
|
||||
if(rtl)
|
||||
x = x + width - visibleWidth;
|
||||
if(mouseX > x - 0.5 && mouseX < x + visibleWidth + 0.5 &&
|
||||
mouseY > y - 0.5 && mouseY < y + height + 0.5)
|
||||
this._controlsInitiallyHovered = true;
|
||||
}
|
||||
|
||||
this._zoomOut = this._alwaysZoomOut;
|
||||
this._zoomFraction = this._alwaysZoomOut ? 1 : 0;
|
||||
this._updateZoom();
|
||||
|
||||
this._controls.show();
|
||||
this._thumbnailsBox.show();
|
||||
|
||||
this._updateWorkspacesViews();
|
||||
|
||||
this._restackedNotifyId =
|
||||
global.screen.connect('restacked',
|
||||
Main.overview.connect('sync-window-stacking',
|
||||
Lang.bind(this, this._onRestacked));
|
||||
|
||||
if (this._itemDragBeginId == 0)
|
||||
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
|
||||
if (this._appDragBeginId == 0)
|
||||
this._appDragBeginId = Main.overview.connect('app-drag-begin',
|
||||
Lang.bind(this, this._dragBegin));
|
||||
if (this._itemDragCancelledId == 0)
|
||||
this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled',
|
||||
if (this._appDragCancelledId == 0)
|
||||
this._appDragCancelledId = Main.overview.connect('app-drag-cancelled',
|
||||
Lang.bind(this, this._dragCancelled));
|
||||
if (this._itemDragEndId == 0)
|
||||
this._itemDragEndId = Main.overview.connect('item-drag-end',
|
||||
if (this._appDragEndId == 0)
|
||||
this._appDragEndId = Main.overview.connect('app-drag-end',
|
||||
Lang.bind(this, this._dragEnd));
|
||||
if (this._windowDragBeginId == 0)
|
||||
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
|
||||
@ -588,8 +523,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
if (this._windowDragEndId == 0)
|
||||
this._windowDragEndId = Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._dragEnd));
|
||||
|
||||
this._onRestacked();
|
||||
},
|
||||
|
||||
zoomFromOverview: function() {
|
||||
@ -599,27 +532,21 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._controls.hide();
|
||||
this._thumbnailsBox.hide();
|
||||
|
||||
if (!this._alwaysZoomOut)
|
||||
this.zoomFraction = 0;
|
||||
|
||||
if (this._restackedNotifyId > 0){
|
||||
global.screen.disconnect(this._restackedNotifyId);
|
||||
Main.overview.disconnect(this._restackedNotifyId);
|
||||
this._restackedNotifyId = 0;
|
||||
}
|
||||
if (this._itemDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._itemDragBeginId);
|
||||
this._itemDragBeginId = 0;
|
||||
if (this._appDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._appDragBeginId);
|
||||
this._appDragBeginId = 0;
|
||||
}
|
||||
if (this._itemDragCancelledId > 0) {
|
||||
Main.overview.disconnect(this._itemDragCancelledId);
|
||||
this._itemDragCancelledId = 0;
|
||||
if (this._appDragCancelledId > 0) {
|
||||
Main.overview.disconnect(this._appDragCancelledId);
|
||||
this._appDragCancelledId = 0;
|
||||
}
|
||||
if (this._itemDragEndId > 0) {
|
||||
Main.overview.disconnect(this._itemDragEndId);
|
||||
this._itemDragEndId = 0;
|
||||
if (this._appDragEndId > 0) {
|
||||
Main.overview.disconnect(this._appDragEndId);
|
||||
this._appDragEndId = 0;
|
||||
}
|
||||
if (this._windowDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._windowDragBeginId);
|
||||
@ -750,73 +677,15 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
|
||||
},
|
||||
|
||||
// zoomFraction property allows us to tween the controls sliding in and out
|
||||
set zoomFraction(fraction) {
|
||||
this._zoomFraction = fraction;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get zoomFraction() {
|
||||
return this._zoomFraction;
|
||||
},
|
||||
|
||||
_updateAlwaysZoom: function() {
|
||||
// Always show the pager if workspaces are actually used,
|
||||
// e.g. there are windows on more than one
|
||||
this._alwaysZoomOut = global.screen.n_workspaces > 2;
|
||||
|
||||
if (this._alwaysZoomOut)
|
||||
return;
|
||||
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
/* Look for any monitor to the right of the primary, if there is
|
||||
* one, we always keep zoom out, otherwise its hard to reach
|
||||
* the thumbnail area without passing into the next monitor. */
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (monitors[i].x >= primary.x + primary.width) {
|
||||
this._alwaysZoomOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
// pass through the call in case the child needs it, but report 0x0
|
||||
this._controls.get_preferred_width(forHeight);
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
// pass through the call in case the child needs it, but report 0x0
|
||||
this._controls.get_preferred_height(forWidth);
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
let totalWidth = box.x2 - box.x1;
|
||||
|
||||
// width of the controls
|
||||
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(box.y2 - box.y1);
|
||||
|
||||
// Amount of space on the screen we reserve for the visible control
|
||||
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
|
||||
let controlsReserved = controlsVisible * (1 - this._zoomFraction) + controlsNatural * this._zoomFraction;
|
||||
|
||||
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
|
||||
if (rtl) {
|
||||
childBox.x2 = controlsReserved;
|
||||
childBox.x1 = childBox.x2 - controlsNatural;
|
||||
} else {
|
||||
childBox.x1 = totalWidth - controlsReserved;
|
||||
childBox.x2 = childBox.x1 + controlsNatural;
|
||||
}
|
||||
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = box.y2- box.y1;
|
||||
this._controls.allocate(childBox, flags);
|
||||
|
||||
this._updateWorkspacesGeometry();
|
||||
},
|
||||
|
||||
@ -856,36 +725,22 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
let width = fullWidth;
|
||||
let height = fullHeight;
|
||||
|
||||
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(height);
|
||||
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
|
||||
|
||||
let [x, y] = this.actor.get_transformed_position();
|
||||
|
||||
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
|
||||
|
||||
let clipWidth = width - controlsVisible;
|
||||
let clipHeight = (fullHeight / fullWidth) * clipWidth;
|
||||
let clipX = rtl ? x + controlsVisible : x;
|
||||
let clipWidth = width;
|
||||
let clipHeight = fullHeight;
|
||||
let clipX = x;
|
||||
let clipY = y + (fullHeight - clipHeight) / 2;
|
||||
|
||||
let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
|
||||
width -= widthAdjust;
|
||||
if (rtl)
|
||||
x += widthAdjust;
|
||||
|
||||
height = (fullHeight / fullWidth) * width;
|
||||
let difference = fullHeight - height;
|
||||
y += difference / 2;
|
||||
|
||||
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let m = 0;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (i == this._primaryIndex) {
|
||||
this._workspacesViews[m].setClipRect(clipX, clipY,
|
||||
clipWidth, clipHeight);
|
||||
this._workspacesViews[m].setGeometry(x, y, width, height,
|
||||
difference);
|
||||
this._workspacesViews[m].setGeometry(x, y, width, height);
|
||||
m++;
|
||||
} else if (!this._workspacesOnlyOnPrimary) {
|
||||
this._workspacesViews[m].setClipRect(monitors[i].x,
|
||||
@ -895,31 +750,18 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._workspacesViews[m].setGeometry(monitors[i].x,
|
||||
monitors[i].y,
|
||||
monitors[i].width,
|
||||
monitors[i].height, 0);
|
||||
monitors[i].height);
|
||||
m++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onRestacked: function() {
|
||||
let stack = global.get_window_actors();
|
||||
let stackIndices = {};
|
||||
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
// Use the stable sequence for an integer to use as a hash key
|
||||
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||
}
|
||||
|
||||
_onRestacked: function(actor, stackIndices) {
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].syncStacking(stackIndices);
|
||||
|
||||
this._thumbnailsBox.syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
_workspacesChanged: function() {
|
||||
this._updateAlwaysZoom();
|
||||
this._updateZoom();
|
||||
|
||||
if (this._workspacesViews == null)
|
||||
return;
|
||||
|
||||
@ -932,19 +774,18 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let m = 0;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (this._workspacesOnlyOnPrimaryChanged &&
|
||||
if (this._workspacesOnlyOnPrimary &&
|
||||
i != this._primaryIndex)
|
||||
continue;
|
||||
|
||||
// Assume workspaces are only added at the end
|
||||
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(w);
|
||||
this._workspaces[m++][w] =
|
||||
this._workspaces[m][w] =
|
||||
new Workspace.Workspace(metaWorkspace, i);
|
||||
}
|
||||
m++;
|
||||
}
|
||||
|
||||
this._thumbnailsBox.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
|
||||
} else {
|
||||
// Assume workspaces are only removed sequentially
|
||||
// (e.g. 2,3,4 - not 2,4,7)
|
||||
@ -967,42 +808,12 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
lostWorkspaces[l].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
this._thumbnailsBox.removeThumbmails(removedIndex, removedNum);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces,
|
||||
newNumWorkspaces);
|
||||
},
|
||||
|
||||
_updateZoom : function() {
|
||||
if (Main.overview.animationInProgress)
|
||||
return;
|
||||
|
||||
let shouldZoom = this._alwaysZoomOut || this._controls.hover;
|
||||
if (shouldZoom != this._zoomOut) {
|
||||
this._zoomOut = shouldZoom;
|
||||
this._updateWorkspacesGeometry();
|
||||
|
||||
if (!this._workspacesViews)
|
||||
return;
|
||||
|
||||
Tweener.addTween(this,
|
||||
{ zoomFraction: this._zoomOut ? 1 : 0,
|
||||
time: WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].updateWindowPositions();
|
||||
}
|
||||
},
|
||||
|
||||
_onControlsHoverChanged: function() {
|
||||
if(!this._controls.hover)
|
||||
this._controlsInitiallyHovered = false;
|
||||
if(!this._controlsInitiallyHovered)
|
||||
this._updateZoom();
|
||||
this._updateSwitcherVisibility();
|
||||
},
|
||||
|
||||
_dragBegin: function() {
|
||||
@ -1020,33 +831,11 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDragMotion: function(dragEvent) {
|
||||
let controlsHovered = this._controls.contains(dragEvent.targetActor);
|
||||
this._controls.set_hover(controlsHovered);
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_dragEnd: function() {
|
||||
this._inDrag = false;
|
||||
|
||||
// We do this deferred because drag-end is emitted before dnd.js emits
|
||||
// event/leave events that were suppressed during the drag. If we didn't
|
||||
// defer this, we'd zoom out then immediately zoom in because of the
|
||||
// enter event we received. That would normally be invisible but we
|
||||
// might as well avoid it.
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, this._updateZoom));
|
||||
},
|
||||
|
||||
_onScrollEvent: function (actor, event) {
|
||||
switch ( event.get_scroll_direction() ) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(WorkspacesDisplay.prototype);
|
||||
|
@ -34,6 +34,7 @@ kk
|
||||
kn
|
||||
ko
|
||||
ku
|
||||
ky
|
||||
lt
|
||||
lv
|
||||
ml
|
||||
|
@ -1,3 +1,5 @@
|
||||
data/50-gnome-shell-screenshot.xml.in
|
||||
data/50-gnome-shell-system.xml.in
|
||||
data/gnome-shell.desktop.in.in
|
||||
data/gnome-shell-extension-prefs.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in.in
|
||||
@ -8,24 +10,25 @@ js/gdm/util.js
|
||||
js/misc/util.js
|
||||
js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/autorunManager.js
|
||||
js/ui/calendar.js
|
||||
js/ui/components/autorunManager.js
|
||||
js/ui/components/keyring.js
|
||||
js/ui/components/networkAgent.js
|
||||
js/ui/components/polkitAgent.js
|
||||
js/ui/components/recorder.js
|
||||
js/ui/components/telepathyClient.js
|
||||
js/ui/dash.js
|
||||
js/ui/dateMenu.js
|
||||
js/ui/endSessionDialog.js
|
||||
js/ui/extensionDownloader.js
|
||||
js/ui/extensionSystem.js
|
||||
js/ui/keyboard.js
|
||||
js/ui/keyringPrompt.js
|
||||
js/ui/lookingGlass.js
|
||||
js/ui/main.js
|
||||
js/ui/messageTray.js
|
||||
js/ui/networkAgent.js
|
||||
js/ui/notificationDaemon.js
|
||||
js/ui/overview.js
|
||||
js/ui/panel.js
|
||||
js/ui/placeDisplay.js
|
||||
js/ui/polkitAuthenticationAgent.js
|
||||
js/ui/popupMenu.js
|
||||
js/ui/runDialog.js
|
||||
js/ui/screenShield.js
|
||||
@ -39,7 +42,6 @@ js/ui/status/lockScreenMenu.js
|
||||
js/ui/status/network.js
|
||||
js/ui/status/power.js
|
||||
js/ui/status/volume.js
|
||||
js/ui/telepathyClient.js
|
||||
js/ui/unlockDialog.js
|
||||
js/ui/userMenu.js
|
||||
js/ui/viewSelector.js
|
||||
|
1649
po/ca@valencia.po
1649
po/ca@valencia.po
File diff suppressed because it is too large
Load Diff
1633
po/en_GB.po
1633
po/en_GB.po
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user