Compare commits
	
		
			243 Commits
		
	
	
		
			3.35.1
			...
			wip/carlos
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 9115f6e796 | ||
|   | 0223d38602 | ||
|   | 33c10e9180 | ||
|   | c7dec4130d | ||
|   | 512130172c | ||
|   | a849945bc4 | ||
|   | 4d16d2ceed | ||
|   | 9a45d9692a | ||
|   | 4a6c2f1fe6 | ||
|   | 086ba11621 | ||
|   | 154655838d | ||
|   | 0795d8df5f | ||
|   | 85bec783ee | ||
|   | ccd8b47d30 | ||
|   | c0309d9732 | ||
|   | 0185c288c3 | ||
|   | 3c4c37e4d0 | ||
|   | 2ba4108838 | ||
|   | 203c3f9949 | ||
|   | 61b71998a0 | ||
|   | c4fa052b03 | ||
|   | f3eeb94c90 | ||
|   | a2044c61ae | ||
|   | 2703eed446 | ||
|   | 59a43f496d | ||
|   | d28bc7afe6 | ||
|   | 22cb0b002d | ||
|   | 021f3e49b5 | ||
|   | 91bf7f1e44 | ||
|   | 7fbdaadce2 | ||
|   | ff7dfa9259 | ||
|   | 85f10f1f6a | ||
|   | 9b4780fa1d | ||
|   | 8c4d07ba92 | ||
|   | 632a643994 | ||
|   | 988a0e7314 | ||
|   | 73776508b3 | ||
|   | 01c0803a4a | ||
|   | 582bfe830a | ||
|   | 236bdaa53c | ||
|   | 52f5793c9b | ||
|   | 1e8e08ce61 | ||
|   | 05c3ac2359 | ||
|   | 47758d16ff | ||
|   | 867cffaf20 | ||
|   | 5f9036e815 | ||
|   | 5b957f69d8 | ||
|   | 749a4c9f6c | ||
|   | 0a9e1b4173 | ||
|   | 66f4feeb16 | ||
|   | e642e1c106 | ||
|   | d9ef612302 | ||
|   | f742484795 | ||
|   | 1ecdb393d7 | ||
|   | eee1ab4890 | ||
|   | 42eb9f4a28 | ||
|   | 18421e8aed | ||
|   | c255b4d14e | ||
|   | bb48205aae | ||
|   | 5a287a4205 | ||
|   | b0c8192496 | ||
|   | 0897915b05 | ||
|   | 2894085c45 | ||
|   | c506eda20a | ||
|   | a8005e3c30 | ||
|   | 6c8eb1a18e | ||
|   | 5af8bf2788 | ||
|   | 7e9f30da0a | ||
|   | 2842670082 | ||
|   | 95f388b9a7 | ||
|   | e72c38b5ab | ||
|   | f6f373b0c2 | ||
|   | b757f5c655 | ||
|   | 784c0b7e4b | ||
|   | f2df9f1ae4 | ||
|   | 18a1435c25 | ||
|   | 669d12f957 | ||
|   | d52b23dec3 | ||
|   | 998fe58482 | ||
|   | 109f39afa5 | ||
|   | 9790b0ee5d | ||
|   | c48330a986 | ||
|   | 9132063b87 | ||
|   | d5e8f8cdf7 | ||
|   | 55362aed3d | ||
|   | 135d178d08 | ||
|   | e7b9bd75d8 | ||
|   | bd173ac5d2 | ||
|   | bfc7c1cd65 | ||
|   | cae69b3a88 | ||
|   | 910037f014 | ||
|   | acaa9f7f77 | ||
|   | b779f6f728 | ||
|   | 83f224e08b | ||
|   | c1a7c71549 | ||
|   | c68bd33432 | ||
|   | 8f4e91a738 | ||
|   | 6af25b282c | ||
|   | e3e1a27f2d | ||
|   | c1ec7b2ffa | ||
|   | 45ebb94b33 | ||
|   | 89bf360bad | ||
|   | 3a7228cf2f | ||
|   | c1ae634174 | ||
|   | 88bcaafe86 | ||
|   | 66fc5c07bb | ||
|   | a32c4f30d1 | ||
|   | 65c5cfd4dc | ||
|   | 0483c78dd1 | ||
|   | abc7cc9a26 | ||
|   | 913990b9ea | ||
|   | 61210fdae1 | ||
|   | 077d8f33fb | ||
|   | e44adb92cf | ||
|   | ebf77748a8 | ||
|   | 07cc84f632 | ||
|   | c860409da5 | ||
|   | 9eaa0089d0 | ||
|   | 682bd7e97c | ||
|   | 2e4e2500dd | ||
|   | 67ea424525 | ||
|   | 69f63dc94f | ||
|   | bef5043135 | ||
|   | fea5ecc9e8 | ||
|   | 697912d8a4 | ||
|   | a0b0237689 | ||
|   | 8eb5d5aac5 | ||
|   | f28f041a95 | ||
|   | cd84fa824f | ||
|   | 40bd65c9ba | ||
|   | 5e43f282a1 | ||
|   | d83d8f2c45 | ||
|   | 348e4ac901 | ||
|   | 5944a1e74b | ||
|   | 0ed702d1af | ||
|   | fc71f8b33a | ||
|   | cb7374b1ec | ||
|   | f5996a9232 | ||
|   | 5fd52e99d3 | ||
|   | fd5989e99a | ||
|   | cf6beee9e2 | ||
|   | be5f5ec9d4 | ||
|   | a58bdbfbd4 | ||
|   | f51952f5d6 | ||
|   | ac1f896107 | ||
|   | 3913fa5044 | ||
|   | 32185c17d0 | ||
|   | d3d165243c | ||
|   | 1e203f4631 | ||
|   | 0617be9fb9 | ||
|   | 0749ac27ce | ||
|   | d5eafbad87 | ||
|   | 5c7a701a68 | ||
|   | cd36301d2b | ||
|   | 70203b58ca | ||
|   | c627d47019 | ||
|   | f546715cc3 | ||
|   | f5e179f03d | ||
|   | 35a265a183 | ||
|   | 147a743d8d | ||
|   | e4147f3611 | ||
|   | 6a42d77261 | ||
|   | 55867c40c4 | ||
|   | 28c535e341 | ||
|   | f309d98bc8 | ||
|   | 2c62e45168 | ||
|   | f2bd39b20c | ||
|   | 72af64d964 | ||
|   | 2f39bd8ba4 | ||
|   | f0a5170473 | ||
|   | 104071acbd | ||
|   | 4338ca5fd9 | ||
|   | e06421b04b | ||
|   | 5687035c9b | ||
|   | 8cb819926a | ||
|   | 284ace5b5f | ||
|   | 7bc39ba750 | ||
|   | aa9031d8e7 | ||
|   | 4dc44304df | ||
|   | 975280fc50 | ||
|   | 39e6fc9e9d | ||
|   | 91707f4f82 | ||
|   | b7bf9e09e9 | ||
|   | db9249a1b7 | ||
|   | 10b2083d3e | ||
|   | fa1b7a9ef5 | ||
|   | b6e57a5ae8 | ||
|   | b6d47c18c3 | ||
|   | c35b4cede5 | ||
|   | 6cfcfc72cc | ||
|   | 0732e1426a | ||
|   | 055c007ac2 | ||
|   | 43cf466d09 | ||
|   | 6965781d59 | ||
|   | 80680803aa | ||
|   | 51601f3ead | ||
|   | b25a73c243 | ||
|   | d0690c3952 | ||
|   | f2466caef3 | ||
|   | b1d22d2058 | ||
|   | 6f7e5976e2 | ||
|   | 29543f369f | ||
|   | a144a1c76d | ||
|   | d91927674d | ||
|   | d12cd12e1b | ||
|   | caa50dc1a3 | ||
|   | 55b57421dc | ||
|   | 320df13b65 | ||
|   | e4920b2f80 | ||
|   | c9fbae3408 | ||
|   | a3c6217875 | ||
|   | db7726c5bf | ||
|   | 0b91dee5a9 | ||
|   | 3838220961 | ||
|   | 9bb12f6f87 | ||
|   | 4dea1f801a | ||
|   | 91a5133116 | ||
|   | c4c5c4fd5c | ||
|   | f67b409fc1 | ||
|   | 22fe4e92c7 | ||
|   | 91eb84fa4e | ||
|   | 4e1492c926 | ||
|   | ed97f61750 | ||
|   | b5676a2a5c | ||
|   | 7059dcced3 | ||
|   | c7e0c7eb79 | ||
|   | ff775213a5 | ||
|   | 7f9c709c85 | ||
|   | 74d7d3e259 | ||
|   | 0353a5bf2c | ||
|   | ab6a629955 | ||
|   | 6cad251187 | ||
|   | d7c569c692 | ||
|   | 0615370930 | ||
|   | 7a92a9ba21 | ||
|   | 0199857c5b | ||
|   | 59e3a1a816 | ||
|   | 6533690fff | ||
|   | d0d1845bb6 | ||
|   | 20f4fc7c87 | ||
|   | b4128967a1 | ||
|   | 38ad1d7c13 | ||
|   | f78136182f | 
| @@ -1,6 +0,0 @@ | ||||
| { | ||||
|     "extends": [ | ||||
|         "./lint/eslintrc-gjs.json", | ||||
|         "./lint/eslintrc-shell.json" | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										3
									
								
								.eslintrc.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.eslintrc.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| extends: | ||||
|  - ./lint/eslintrc-gjs.yml | ||||
|  - ./lint/eslintrc-shell.yml | ||||
| @@ -14,7 +14,7 @@ variables: | ||||
|         - merge_requests | ||||
|  | ||||
| check_commit_log: | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v2 | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v3 | ||||
|     stage: review | ||||
|     variables: | ||||
|         GIT_DEPTH: "100" | ||||
| @@ -47,7 +47,7 @@ eslint: | ||||
|         when: always | ||||
|  | ||||
| build: | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v2 | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v3 | ||||
|     stage: build | ||||
|     before_script: | ||||
|         - .gitlab-ci/checkout-mutter.sh | ||||
| @@ -65,14 +65,15 @@ build: | ||||
|             - build | ||||
|  | ||||
| test: | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v2 | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v3 | ||||
|     stage: test | ||||
|     variables: | ||||
|         XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/runtime-dir" | ||||
|         NO_AT_BRIDGE: "1" | ||||
|     before_script: | ||||
|         - ninja -C mutter/build install | ||||
|     script: | ||||
|         - xvfb-run meson test -C build --no-rebuild | ||||
|         - dbus-run-session -- xvfb-run meson test -C build --no-rebuild | ||||
|     <<: *only_default | ||||
|     artifacts: | ||||
|         expire_in: 1 day | ||||
| @@ -81,7 +82,7 @@ test: | ||||
|         when: on_failure | ||||
|  | ||||
| test-pot: | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v2 | ||||
|     image: registry.gitlab.gnome.org/gnome/mutter/master:v3 | ||||
|     stage: test | ||||
|     before_script: | ||||
|         - ninja -C mutter/build install | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| #!/usr/bin/bash | ||||
|  | ||||
| shell_branch=$(git describe --contains --all HEAD) | ||||
| mutter_target= | ||||
|  | ||||
| git clone https://gitlab.gnome.org/GNOME/mutter.git | ||||
| @@ -26,8 +25,7 @@ if [ "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then | ||||
| fi | ||||
|  | ||||
| if [ -z "$mutter_target" ]; then | ||||
|   mutter_target=$(git branch -r -l origin/$shell_branch) | ||||
|   mutter_target=${mutter_target:-$(git branch -r -l ${shell_branch#remotes/})} | ||||
|   mutter_target=$(git branch -r -l origin/$CI_COMMIT_REF_NAME) | ||||
|   mutter_target=${mutter_target:-origin/master} | ||||
|   echo Using $mutter_target instead | ||||
| fi | ||||
|   | ||||
| @@ -13,11 +13,17 @@ is_empty() { | ||||
| } | ||||
|  | ||||
| run_eslint() { | ||||
|   ARGS_LEGACY='--config lint/eslintrc-legacy.json' | ||||
|   ARGS_LEGACY='--config lint/eslintrc-legacy.yml' | ||||
|  | ||||
|   local extra_args=ARGS_$1 | ||||
|   local output=OUTPUT_$1 | ||||
|   eslint -f unix ${!extra_args} -o ${!output} js | ||||
|   local output_var=OUTPUT_$1 | ||||
|   local output=${!output_var} | ||||
|  | ||||
|   # ensure output exists even if eslint doesn't report any errors | ||||
|   mkdir -p $(dirname $output) | ||||
|   touch $output | ||||
|  | ||||
|   eslint -f unix ${!extra_args} -o $output js | ||||
| } | ||||
|  | ||||
| list_commit_range_additions() { | ||||
| @@ -70,10 +76,13 @@ create_common() { | ||||
| # non-legacy style just yet ... | ||||
| unset CI_MERGE_REQUEST_TARGET_BRANCH_NAME | ||||
|  | ||||
| if [ "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then | ||||
|   git fetch $CI_MERGE_REQUEST_PROJECT_URL.git $CI_MERGE_REQUEST_TARGET_BRANCH_NAME | ||||
| REMOTE=${1:-$CI_MERGE_REQUEST_PROJECT_URL.git} | ||||
| BRANCH_NAME=${2:-$CI_MERGE_REQUEST_TARGET_BRANCH_NAME} | ||||
|  | ||||
| if [ "$BRANCH_NAME" ]; then | ||||
|   git fetch $REMOTE $BRANCH_NAME | ||||
|   branch_point=$(git merge-base HEAD FETCH_HEAD) | ||||
|   commit_range=$branch_point...$CI_COMMIT_SHA | ||||
|   commit_range=$branch_point...HEAD | ||||
|  | ||||
|   list_commit_range_additions $commit_range > $LINE_CHANGES | ||||
|  | ||||
| @@ -96,7 +105,7 @@ if ! is_empty $OUTPUT_FINAL; then | ||||
| fi | ||||
|  | ||||
| # Just show the report and succeed when not testing a MR | ||||
| if [ -z "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then | ||||
| if [ -z "$BRANCH_NAME" ]; then | ||||
|   exit 0 | ||||
| fi | ||||
|  | ||||
|   | ||||
							
								
								
									
										33
									
								
								HACKING.md
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								HACKING.md
									
									
									
									
									
								
							| @@ -163,11 +163,17 @@ you to inherit from a type to use it, you can do so: | ||||
|              return [100, 100]; | ||||
|         } | ||||
|  | ||||
|         vfunc_paint() { | ||||
|         vfunc_paint(paintContext) { | ||||
|              let framebuffer = paintContext.get_framebuffer(); | ||||
|              let coglContext = framebuffer.get_context(); | ||||
|              let alloc = this.get_allocation_box(); | ||||
|              Cogl.set_source_color4ub(255, 0, 0, 255); | ||||
|              Cogl.rectangle(alloc.x1, alloc.y1, | ||||
|                             alloc.x2, alloc.y2); | ||||
|  | ||||
|              let pipeline = new Cogl.Pipeline(coglContext); | ||||
|              pipeline.set_color4ub(255, 0, 0, 255); | ||||
|  | ||||
|              framebuffer.draw_rectangle(pipeline, | ||||
| 		 alloc.x1, alloc.y1, | ||||
| 		 alloc.x2, alloc.y2); | ||||
|         } | ||||
|     }); | ||||
| ``` | ||||
| @@ -186,15 +192,27 @@ and "double quotes" for strings that the user may see. This allows us to | ||||
| quickly find untranslated or mistranslated strings by grepping through the | ||||
| sources for double quotes without a gettext call around them. | ||||
|  | ||||
| ## `actor` and `_delegate` | ||||
| ## `actor` (deprecated) and `_delegate` | ||||
|  | ||||
| gjs allows us to set so-called "expando properties" on introspected objects, | ||||
| allowing us to treat them like any other. Because the Shell was built before | ||||
| you could inherit from GTypes natively in JS, we usually have a wrapper class | ||||
| that has a property called `actor`. We call this wrapper class the "delegate". | ||||
| you could inherit from GTypes natively in JS, in some cases we have a wrapper | ||||
| class that has a property called `actor` (now deprecated). We call this | ||||
| wrapper class the "delegate". | ||||
|  | ||||
| We sometimes use expando properties to set a property called `_delegate` on | ||||
| the actor itself: | ||||
| ```javascript | ||||
|     var MyActor = GObject.registerClass( | ||||
|     class MyActor extends Clutter.Actor { | ||||
|         _init(params) { | ||||
|             super._init(params); | ||||
|             this._delegate = this; | ||||
|         } | ||||
|     }); | ||||
| ``` | ||||
|  | ||||
| Or using the deprecated `actor`: | ||||
| ```javascript | ||||
|     var MyClass = class { | ||||
|         constructor() { | ||||
| @@ -215,6 +233,7 @@ delegate object from an associated actor. For instance, the drag and drop | ||||
| system calls the `handleDragOver` function on the delegate of a "drop target" | ||||
| when the user drags an item over it. If you do not set the `_delegate` | ||||
| property, your actor will not be able to be dropped onto. | ||||
| In case the class is an actor itself, the `_delegate` can be just set to `this`. | ||||
|  | ||||
| ## Functional style | ||||
|  | ||||
|   | ||||
							
								
								
									
										65
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								NEWS
									
									
									
									
									
								
							| @@ -1,3 +1,68 @@ | ||||
| 3.35.2 | ||||
| ====== | ||||
| * Fix unredirection after cancelled animations [Florian; #1788] | ||||
| * Include shadow in window screenshots [Robert; !762] | ||||
| * Show indicator when microphone is active [Florian; !729] | ||||
| * Use inheritance instead of delegate pattern [Marco; !559] | ||||
| * Use cached coordinates for window sorting in overview [Andrew; !763] | ||||
| * Wiggle login/unlock password entries on failure [Georges; !769] | ||||
| * Update window titles in app menu [Florian; #1830] | ||||
| * Fix window animations getting stuck by workspace switches [Jonas D.; !784] | ||||
| * Fix not-responding dialog size when using geometry scaling [Jonas D.; !783] | ||||
| * Handle buggy MPRIS clients more gracefully [Philip; #1362] | ||||
| * Deprecate StBoxLayout's child properties [Florian; !780] | ||||
| * Remove StBin's align properties [Florian; !803] | ||||
| * Use correct timezones for events [Milan, Florian; !806, #1895] | ||||
| * Reduce overhead of tracking stylesheet changes [Carlos; !779] | ||||
| * Replace action icons in system menu with regular menu items [Florian; #270] | ||||
| * Refine polkit dialogs [Jonas D.; !788] | ||||
| * Fix battery icon glitch in "100% but charging" case [Philip; !814] | ||||
| * Fix windows getting stuck on screen if closed while animating [Florian; !815] | ||||
| * Use font from interface settings [Florian; #688288] | ||||
| * Show polkit confirmation dialog for users with no password | ||||
|   [Joaquim, Jonas D.; !829] | ||||
| * Use better OSK layout fallback for unsupported variants [Florian; #1907] | ||||
| * Hide stopped spinner in top bar [Joonas; !832] | ||||
| * Reuse existing icons when updating the app picker grid [Georges; !841] | ||||
| * Show switcher popups immediately on second key press [Florian; #1928] | ||||
| * Add position-based animation to page indicators [Alexander; !843] | ||||
| * Improve modifier-less keyboard navigation of switcher popups [Florian; #1883] | ||||
| * Improve weather integration [Florian; #1927, #1926] | ||||
| * Add back sound feedback when scrolling volume indicator [Florian; #53] | ||||
| * Fix creating app folders with no pre-existing folders [Jonas D.; #1652] | ||||
| * Improve DND page switching in app picker [Florian, Jonas D.; #1693] | ||||
| * Fix disable command of gnome-extensions tool [Florian; #1946] | ||||
| * Tweak styling of notifications/media constrols [Joonas; !855, !865] | ||||
| * Enable clean session shutdown after gnome-shell failure [Benjamin; !858] | ||||
| * Also remove scaled keys when texture cache is cleared [Daniel M.; !567] | ||||
| * Don't show overflow indicator in switchers that fit screen [Florian; #1834] | ||||
| * Move libcroco dependency in-tree [Federico; !861] | ||||
| * Move to app folder location when it is created/renamed [Georges; !883] | ||||
| * Dismiss switcher popups when a system modal dialogs opens [Florian; #1536] | ||||
| * Fix weather forecasts for automatic location when Weather is not sandboxed | ||||
|   [Florian; #1823] | ||||
| * Place launched applications into a systemd scope [Benjamin; !863] | ||||
| * Fixed crashes [Jonas D., Carlos; !787, !813] | ||||
| * Misc. bug fixes and cleanups [Marco, Georges, Daniel V., Florian, Robert, | ||||
|   Kalev, Philip, Jonas D., Will, Carlos, Jonas Å., cunidev, Joonas, Federico; | ||||
|   !747, !765, !421, !759, !749, !730, !770, #1799, !774, !773, !776, !777, | ||||
|   !782, !794, !778, !792, !790, !190, !796, !795, !797, !798, !800, !804, !808, | ||||
|   !807, !810, !811, !563, !809, !805, !817, !818, !822, !830, !828, !823, !835, | ||||
|   !840, !842, !833, !845, !846, !847, !851, #1916, !862, !866, #1979, !827, | ||||
|   #1976, !884, !873, !885, !799, !887, !891, !816] | ||||
|  | ||||
| Contributors: | ||||
|   Marco Trevisan (Treviño), Benjamin Berg, Philip Chimento, Milan Crha, | ||||
|   Jonas Dreßler, Carlos Garnacho, Joonas Henriksson, Kalev Lember, Robert Mader, | ||||
|   Alexander Mikhaylenko, Daniel García Moreno, Florian Müllner, | ||||
|   Georges Basile Stavracas Neto, Federico Mena Quintero, Joaquim Rocha, | ||||
|   Will Thompson, Daniel van Vugt, Andrew Watson, cunidev, Jonas Ådahl | ||||
|  | ||||
| Translators: | ||||
|   Daniel Mustieles [es], Goran Vidović [hr], Fabio Tomat [fur], | ||||
|   Danial Behzadi [fa], Andika Triwidada [id], Efstathios Iosifidis [el], | ||||
|   Ricardo Silva Veloso [pt_BR] | ||||
|  | ||||
| 3.35.1 | ||||
| ====== | ||||
| * Misc. bug fixes and cleanups [Marco; Matthias; !758, #701212] | ||||
|   | ||||
| @@ -1,5 +1,46 @@ | ||||
| <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||||
| "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||||
|  | ||||
| <node> | ||||
|  | ||||
|   <!-- | ||||
|       net.hadess.SwitcherooControl: | ||||
|       @short_description: D-Bus proxy to access dual-GPU controls. | ||||
|  | ||||
|       After checking the availability of two switchable GPUs in the machine, | ||||
|       check the value of net.hadess.SwitcherooControl.HasDualGpu to see | ||||
|       if running applications on the discrete GPU should be offered. | ||||
|  | ||||
|       The object path will be "/net/hadess/SwitcherooControl". | ||||
|   --> | ||||
|   <interface name="net.hadess.SwitcherooControl"> | ||||
|     <!-- | ||||
|         HasDualGpu: | ||||
|  | ||||
|         Whether two switchable GPUs are present on the system. This property | ||||
|         has been obsoleted in favour of the "NumGPUs" property. | ||||
|     --> | ||||
|     <property name="HasDualGpu" type="b" access="read"/> | ||||
|  | ||||
|     <!-- | ||||
|         NumGPUs: | ||||
|  | ||||
|         The number of GPUs available on the system. Note that while having no | ||||
|         GPUs is unlikely, consumers of this API should probably not throw errors | ||||
|         if that were the case. | ||||
|     --> | ||||
|     <property name="NumGPUs" type="u" access="read"/> | ||||
|  | ||||
|     <!-- | ||||
|         GPUs: | ||||
|  | ||||
|         An array of key-pair values representing each GPU. The key named "Name" (s) | ||||
|         will contain a user-facing name for the GPU, the "Environment" (as) key will | ||||
|         contain an array of even number of strings, each being an environment | ||||
|         variable to set to use the GPU, followed by its value, the "Default" (b) key | ||||
|         will tag the default (usually integrated) GPU. | ||||
|     --> | ||||
|     <property name="GPUs" type="aa{sv}" access="read"/> | ||||
|  | ||||
|   </interface> | ||||
| </node> | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
| [Unit] | ||||
| Description=Disable GNOME Shell extensions after failure | ||||
| # Note that this unit must not conflict with anything, and must | ||||
| # be able to run in parallel with the gnome-session-shutdown.target. | ||||
| DefaultDependencies=no | ||||
|  | ||||
| # Only disable extensions for a short period of time after login. | ||||
| # This means we err on the side of failing the first login after a broken | ||||
| # extension was installed. | ||||
| Requisite=gnome-session-stable.timer | ||||
| # We want to disable extensions only if gnome-shell has flagged the extensions | ||||
| # to be a likely cause of trouble. | ||||
| ConditionPathExists=%t/gnome-shell-disable-extensions | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
|   | ||||
| @@ -50,7 +50,7 @@ | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="favorite-apps" type="as"> | ||||
|       <default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default> | ||||
|       <default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'org.gnome.Shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default> | ||||
|       <summary>List of desktop file IDs for favorite applications</summary> | ||||
|       <description> | ||||
|         The applications corresponding to these identifiers | ||||
|   | ||||
| @@ -36,10 +36,8 @@ $_hover_bg_color: lighten($bg_color,if($variant=='light', 5%, 3%)); | ||||
| $_active_bg_color: if($variant == 'light', darken($bg_color, 14%), darken($bg_color, 9%)); | ||||
|  | ||||
| $font-size: 11; | ||||
| $font-family: Cantarell, Sans-Serif; | ||||
|  | ||||
| stage { | ||||
|   font-family: $font-family; | ||||
|   @include fontsize($font-size); | ||||
|   color: $fg_color; | ||||
| } | ||||
| @@ -979,6 +977,7 @@ StScrollBar { | ||||
|       spacing-columns: 0.8em; | ||||
|     } | ||||
|  | ||||
|     .weather-header-box, | ||||
|     .weather-box { | ||||
|       spacing: 0.4em; | ||||
|     } | ||||
| @@ -1061,9 +1060,9 @@ StScrollBar { | ||||
|       } | ||||
|       .calendar-today { | ||||
|         font-weight: bold; | ||||
|         //color: lighten($fg_color,10%); | ||||
|         //background-color: darken($bg_color,5%); | ||||
|         border: 1px solid $_bubble_borders_color; | ||||
|         color: lighten($fg_color,5%); | ||||
|         background-color: darken($bg_color,5%); | ||||
|         // border: 1px solid lighten($_bubble_borders_color,20%); | ||||
|       } | ||||
|       .calendar-day-with-events { | ||||
|         color: lighten($fg_color,10%); | ||||
| @@ -1153,14 +1152,21 @@ StScrollBar { | ||||
|             padding: 10px; | ||||
|           } | ||||
|  | ||||
|           .message-close-button { | ||||
|             color: lighten($fg_color, 15%); | ||||
|             &:hover { color: if($variant=='light', lighten($fg_color, 30%), darken($fg_color, 10%)); } | ||||
|             &:active { color: if($variant=='light', lighten($fg_color, 40%), darken($fg_color, 20%)); } | ||||
|           } | ||||
|  | ||||
|           .message-media-control { | ||||
|             padding: 12px; | ||||
|             color: lighten($fg_color, 15%); | ||||
|  | ||||
|             &:last-child:ltr { padding-right: 18px; } | ||||
|             &:last-child:rtl { padding-left: 18px; } | ||||
|             &:hover { color: $fg_color; } | ||||
|             &:insensitive { color: darken($fg_color,40%); } | ||||
|             &:hover { color: if($variant=='light', lighten($fg_color, 30%), darken($fg_color, 10%)); } | ||||
|             &:active { color: if($variant=='light', lighten($fg_color, 40%), darken($fg_color, 20%)); } | ||||
|             &:insensitive { color: if($variant=='light', lighten($fg_color, 50%), darken($fg_color, 40%)); } | ||||
|           } | ||||
|  | ||||
|           .media-message-cover-icon { | ||||
| @@ -1190,7 +1196,8 @@ StScrollBar { | ||||
|  | ||||
|   .aggregate-menu { | ||||
|     min-width: 21em; | ||||
|     .popup-menu-icon { padding: 0 4px; } | ||||
|     .popup-menu-icon { padding: 0 4px; | ||||
|                        -st-icon-style: symbolic; } | ||||
|     .popup-sub-menu .popup-menu-item > :first-child { | ||||
|       &:ltr { /* 12px spacing + 2*4px padding */ | ||||
|               padding-left: 20px; margin-left: 1.09em; } | ||||
| @@ -1199,27 +1206,6 @@ StScrollBar { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .system-menu-action { | ||||
|     -st-icon-style: symbolic; | ||||
|     color: $fg_color; | ||||
|     border-radius: 32px; /* wish we could do 50% */ | ||||
|     padding: 13px; | ||||
|     border: 1px solid $_bubble_borders_color; | ||||
|  | ||||
|     &:hover, &:focus { | ||||
|       background-color: $_hover_bg_color; | ||||
|       color: $fg_color; | ||||
|       border: none; | ||||
|       padding: 14px; | ||||
|     } | ||||
|     &:active {  | ||||
|       background-color: $selected_bg_color; | ||||
|       color: $selected_fg_color; | ||||
|     } | ||||
|  | ||||
|     & > StIcon { icon-size: 16px; } | ||||
|   } | ||||
|  | ||||
| // Activities Ripples | ||||
| .ripple-box { | ||||
|   width: 52px; | ||||
| @@ -1571,20 +1557,14 @@ StScrollBar { | ||||
|   } | ||||
|  | ||||
|   .page-indicator { | ||||
|     padding: 15px 20px; | ||||
|     padding: 7px 16px; | ||||
|  | ||||
|     .page-indicator-icon { | ||||
|       width: 12px; | ||||
|       height: 12px; | ||||
|       background-color: transparent; | ||||
|       border: 2px solid rgba(255, 255, 255, 0.4); | ||||
|       border-radius: 12px; | ||||
|       background-color: white; | ||||
|       border-radius: 6px; | ||||
|     } | ||||
|  | ||||
|     &:hover .page-indicator-icon { border-color: white; } | ||||
|     &:active .page-indicator-icon { border: none; margin: 2px; background-color: white; } | ||||
|     &:checked .page-indicator-icon, | ||||
|     &:checked:active .page-indicator-icon { background-color: white;} | ||||
|   } | ||||
|  | ||||
|   .no-frequent-applications-label { @extend %status_text; } | ||||
| @@ -1784,8 +1764,8 @@ StScrollBar { | ||||
|         padding: 4px 4px; | ||||
|  | ||||
|         .page-indicator-icon { | ||||
|             width: 6px; | ||||
|             height: 6px | ||||
|             width: 8px; | ||||
|             height: 8px; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,34 +31,34 @@ its dependencies to build from tarballs.</description> | ||||
|   <programming-language>JavaScript</programming-language> | ||||
|   <programming-language>C</programming-language> | ||||
|  | ||||
|   <maintainer> | ||||
|   <author> | ||||
|     <foaf:Person> | ||||
|       <foaf:name>William Jon McCann</foaf:name> | ||||
|       <foaf:mbox rdf:resource="mailto:jmccann@redhat.com" /> | ||||
|       <gnome:userid>mccann</gnome:userid> | ||||
|     </foaf:Person> | ||||
|   </maintainer> | ||||
|   <maintainer> | ||||
|   </author> | ||||
|   <author> | ||||
|     <foaf:Person> | ||||
|       <foaf:name>Owen Taylor</foaf:name> | ||||
|       <foaf:mbox rdf:resource="mailto:otaylor@redhat.com" /> | ||||
|       <gnome:userid>otaylor</gnome:userid> | ||||
|     </foaf:Person> | ||||
|   </maintainer> | ||||
|   <maintainer> | ||||
|   </author> | ||||
|   <author> | ||||
|     <foaf:Person> | ||||
|       <foaf:name>Colin Walters</foaf:name> | ||||
|       <foaf:mbox rdf:resource="mailto:walters@verbum.org" /> | ||||
|       <gnome:userid>walters</gnome:userid> | ||||
|     </foaf:Person> | ||||
|   </maintainer> | ||||
|   <maintainer> | ||||
|   </author> | ||||
|   <author> | ||||
|     <foaf:Person> | ||||
|       <foaf:name>Marina Zhurakhinskaya</foaf:name> | ||||
|       <foaf:mbox rdf:resource="mailto:marinaz@redhat.com" /> | ||||
|       <gnome:userid>marinaz</gnome:userid> | ||||
|     </foaf:Person> | ||||
|   </maintainer> | ||||
|   </author> | ||||
|   <maintainer> | ||||
|     <foaf:Person> | ||||
|       <foaf:name>Florian Müllner</foaf:name> | ||||
|   | ||||
| @@ -23,14 +23,13 @@ function stripPrefix(string, prefix) { | ||||
|     return string; | ||||
| } | ||||
|  | ||||
| var Application = GObject.registerClass({ | ||||
|     GTypeName: 'ExtensionPrefs_Application' | ||||
| }, class Application extends Gtk.Application { | ||||
| var Application = GObject.registerClass( | ||||
| class Application extends Gtk.Application { | ||||
|     _init() { | ||||
|         GLib.set_prgname('gnome-shell-extension-prefs'); | ||||
|         super._init({ | ||||
|             application_id: 'org.gnome.shell.ExtensionPrefs', | ||||
|             flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE | ||||
|             flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE, | ||||
|         }); | ||||
|  | ||||
|         this._startupUuid = null; | ||||
| @@ -61,12 +60,12 @@ var Application = GObject.registerClass({ | ||||
|  | ||||
|         let dialog = new Gtk.Window({ | ||||
|             modal: !this._skipMainWindow, | ||||
|             type_hint: Gdk.WindowTypeHint.DIALOG | ||||
|             type_hint: Gdk.WindowTypeHint.DIALOG, | ||||
|         }); | ||||
|         dialog.set_titlebar(new Gtk.HeaderBar({ | ||||
|             show_close_button: true, | ||||
|             title: row.name, | ||||
|             visible: true | ||||
|             visible: true, | ||||
|         })); | ||||
|  | ||||
|         if (this._skipMainWindow) { | ||||
| @@ -89,20 +88,20 @@ var Application = GObject.registerClass({ | ||||
|     _buildErrorUI(row, exc) { | ||||
|         let scroll = new Gtk.ScrolledWindow({ | ||||
|             hscrollbar_policy: Gtk.PolicyType.NEVER, | ||||
|             propagate_natural_height: true | ||||
|             propagate_natural_height: true, | ||||
|         }); | ||||
|  | ||||
|         let box = new Gtk.Box({ | ||||
|             orientation: Gtk.Orientation.VERTICAL, | ||||
|             spacing: 12, | ||||
|             margin: 100, | ||||
|             margin_bottom: 60 | ||||
|             margin_bottom: 60, | ||||
|         }); | ||||
|         scroll.add(box); | ||||
|  | ||||
|         let label = new Gtk.Label({ | ||||
|             label: '<span size="x-large">%s</span>'.format(_("Something’s gone wrong")), | ||||
|             use_markup: true | ||||
|             use_markup: true, | ||||
|         }); | ||||
|         label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); | ||||
|         box.add(label); | ||||
| @@ -110,13 +109,13 @@ var Application = GObject.registerClass({ | ||||
|         label = new Gtk.Label({ | ||||
|             label: _("We’re very sorry, but there’s been a problem: the settings for this extension can’t be displayed. We recommend that you report the issue to the extension authors."), | ||||
|             justify: Gtk.Justification.CENTER, | ||||
|             wrap: true | ||||
|             wrap: true, | ||||
|         }); | ||||
|         box.add(label); | ||||
|  | ||||
|         let expander = new Expander({ | ||||
|             label: _("Technical Details"), | ||||
|             margin_top: 12 | ||||
|             margin_top: 12, | ||||
|         }); | ||||
|         box.add(expander); | ||||
|  | ||||
| @@ -127,14 +126,14 @@ var Application = GObject.registerClass({ | ||||
|  | ||||
|         let buffer = new Gtk.TextBuffer({ text: errortext }); | ||||
|         let textview = new Gtk.TextView({ | ||||
|             buffer: buffer, | ||||
|             buffer, | ||||
|             wrap_mode: Gtk.WrapMode.WORD, | ||||
|             monospace: true, | ||||
|             editable: false, | ||||
|             top_margin: 12, | ||||
|             bottom_margin: 12, | ||||
|             left_margin: 12, | ||||
|             right_margin: 12 | ||||
|             right_margin: 12, | ||||
|         }); | ||||
|  | ||||
|         let toolbar = new Gtk.Toolbar(); | ||||
| @@ -150,7 +149,7 @@ var Application = GObject.registerClass({ | ||||
|  | ||||
|         let copyButton = new Gtk.ToolButton({ | ||||
|             icon_name: 'edit-copy-symbolic', | ||||
|             tooltip_text: _("Copy Error") | ||||
|             tooltip_text: _("Copy Error"), | ||||
|         }); | ||||
|         toolbar.add(copyButton); | ||||
|  | ||||
| @@ -159,15 +158,15 @@ var Application = GObject.registerClass({ | ||||
|             // markdown for pasting in gitlab issues | ||||
|             let lines = [ | ||||
|                 `The settings of extension ${row.uuid} had an error:`, | ||||
|                 '```', | ||||
|                 '```', // '`' (xgettext throws up on odd number of backticks) | ||||
|                 `${exc}`, | ||||
|                 '```', | ||||
|                 '```', // '`' | ||||
|                 '', | ||||
|                 'Stack trace:', | ||||
|                 '```', | ||||
|                 '```', // '`' | ||||
|                 exc.stack.replace(/\n$/, ''), // stack without trailing newline | ||||
|                 '```', | ||||
|                 '' | ||||
|                 '```', // '`' | ||||
|                 '', | ||||
|             ]; | ||||
|             clipboard.set_text(lines.join('\n'), -1); | ||||
|         }); | ||||
| @@ -180,7 +179,7 @@ var Application = GObject.registerClass({ | ||||
|             label: _("Homepage"), | ||||
|             tooltip_text: _("Visit extension homepage"), | ||||
|             no_show_all: true, | ||||
|             visible: row.url != null | ||||
|             visible: row.url != null, | ||||
|         }); | ||||
|         toolbar.add(urlButton); | ||||
|  | ||||
| @@ -190,7 +189,7 @@ var Application = GObject.registerClass({ | ||||
|         }); | ||||
|  | ||||
|         let expandedBox = new Gtk.Box({ | ||||
|             orientation: Gtk.Orientation.VERTICAL | ||||
|             orientation: Gtk.Orientation.VERTICAL, | ||||
|         }); | ||||
|         expandedBox.add(textview); | ||||
|         expandedBox.add(toolbar); | ||||
| @@ -220,7 +219,7 @@ var Application = GObject.registerClass({ | ||||
|                             Gio.SettingsBindFlags.INVERT_BOOLEAN); | ||||
|  | ||||
|         this._mainStack = new Gtk.Stack({ | ||||
|             transition_type: Gtk.StackTransitionType.CROSSFADE | ||||
|             transition_type: Gtk.StackTransitionType.CROSSFADE, | ||||
|         }); | ||||
|         this._window.add(this._mainStack); | ||||
|  | ||||
| @@ -259,23 +258,15 @@ var Application = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     _onExtensionStateChanged(proxy, senderName, [uuid, newState]) { | ||||
|         let extension = ExtensionUtils.deserializeExtension(newState); | ||||
|         let row = this._findExtensionRow(uuid); | ||||
|  | ||||
|         if (row) { | ||||
|             let { state } = ExtensionUtils.deserializeExtension(newState); | ||||
|             if (state == ExtensionState.UNINSTALLED) | ||||
|             if (extension.state === ExtensionState.UNINSTALLED) | ||||
|                 row.destroy(); | ||||
|             return; // we only deal with new and deleted extensions here | ||||
|         } | ||||
|  | ||||
|         this._shellProxy.GetExtensionInfoRemote(uuid, ([serialized]) => { | ||||
|             let extension = ExtensionUtils.deserializeExtension(serialized); | ||||
|             if (!extension) | ||||
|                 return; | ||||
|             // check the extension wasn't added in between | ||||
|             if (this._findExtensionRow(uuid) != null) | ||||
|                 return; | ||||
|             this._addExtensionRow(extension); | ||||
|         }); | ||||
|         this._addExtensionRow(extension); | ||||
|     } | ||||
|  | ||||
|     _scanExtensions() { | ||||
| @@ -361,20 +352,20 @@ var Expander = GObject.registerClass({ | ||||
|             'label', 'label', 'label', | ||||
|             GObject.ParamFlags.READWRITE, | ||||
|             null | ||||
|         ) | ||||
|     } | ||||
|         ), | ||||
|     }, | ||||
| }, class Expander extends Gtk.Box { | ||||
|     _init(params = {}) { | ||||
|         this._labelText = null; | ||||
|  | ||||
|         super._init(Object.assign(params, { | ||||
|             orientation: Gtk.Orientation.VERTICAL, | ||||
|             spacing: 0 | ||||
|             spacing: 0, | ||||
|         })); | ||||
|  | ||||
|         this._frame = new Gtk.Frame({ | ||||
|             shadow_type: Gtk.ShadowType.IN, | ||||
|             hexpand: true | ||||
|             hexpand: true, | ||||
|         }); | ||||
|  | ||||
|         let eventBox = new Gtk.EventBox(); | ||||
| @@ -382,12 +373,12 @@ var Expander = GObject.registerClass({ | ||||
|  | ||||
|         let hbox = new Gtk.Box({ | ||||
|             spacing: 6, | ||||
|             margin: 12 | ||||
|             margin: 12, | ||||
|         }); | ||||
|         eventBox.add(hbox); | ||||
|  | ||||
|         this._arrow = new Gtk.Image({ | ||||
|             icon_name: 'pan-end-symbolic' | ||||
|             icon_name: 'pan-end-symbolic', | ||||
|         }); | ||||
|         hbox.add(this._arrow); | ||||
|  | ||||
| @@ -397,7 +388,7 @@ var Expander = GObject.registerClass({ | ||||
|         this._revealer = new Gtk.Revealer(); | ||||
|  | ||||
|         this._childBin = new Gtk.Frame({ | ||||
|             shadow_type: Gtk.ShadowType.IN | ||||
|             shadow_type: Gtk.ShadowType.IN, | ||||
|         }); | ||||
|         this._revealer.add(this._childBin); | ||||
|  | ||||
| @@ -415,7 +406,7 @@ var Expander = GObject.registerClass({ | ||||
|         this._gesture = new Gtk.GestureMultiPress({ | ||||
|             widget: this._frame, | ||||
|             button: 0, | ||||
|             exclusive: true | ||||
|             exclusive: true, | ||||
|         }); | ||||
|         this._gesture.connect('released', (gesture, nPress) => { | ||||
|             if (nPress == 1) | ||||
| @@ -460,7 +451,7 @@ class EmptyPlaceholder extends Gtk.Box { | ||||
|         super._init({ | ||||
|             orientation: Gtk.Orientation.VERTICAL, | ||||
|             spacing: 6, | ||||
|             margin: 32 | ||||
|             margin: 32, | ||||
|         }); | ||||
|  | ||||
|         let image = new Gtk.Image({ | ||||
| @@ -468,15 +459,15 @@ class EmptyPlaceholder extends Gtk.Box { | ||||
|             pixel_size: 96, | ||||
|             visible: true, | ||||
|             vexpand: true, | ||||
|             valign: Gtk.Align.END | ||||
|             valign: Gtk.Align.END, | ||||
|         }); | ||||
|         image.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); | ||||
|         this.add(image); | ||||
|  | ||||
|         let label = new Gtk.Label({ | ||||
|             label: `<b><span size="x-large">${_("No Extensions Installed" )}</span></b>`, | ||||
|             label: `<b><span size="x-large">${_("No Extensions Installed")}</span></b>`, | ||||
|             use_markup: true, | ||||
|             visible: true | ||||
|             visible: true, | ||||
|         }); | ||||
|         label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); | ||||
|         this.add(label); | ||||
| @@ -491,9 +482,9 @@ class EmptyPlaceholder extends Gtk.Box { | ||||
|             visible: true, | ||||
|             max_width_chars: 50, | ||||
|             hexpand: true, | ||||
|             vexpand: (appInfo == null), | ||||
|             vexpand: appInfo == null, | ||||
|             halign: Gtk.Align.CENTER, | ||||
|             valign: Gtk.Align.START | ||||
|             valign: Gtk.Align.START, | ||||
|         }); | ||||
|         this.add(desc); | ||||
|  | ||||
| @@ -501,14 +492,14 @@ class EmptyPlaceholder extends Gtk.Box { | ||||
|             let button = new Gtk.Button({ | ||||
|                 label: _("Browse in Software"), | ||||
|                 image: new Gtk.Image({ | ||||
|                     icon_name: "org.gnome.Software-symbolic" | ||||
|                     icon_name: "org.gnome.Software-symbolic", | ||||
|                 }), | ||||
|                 always_show_image: true, | ||||
|                 margin_top: 12, | ||||
|                 visible: true, | ||||
|                 halign: Gtk.Align.CENTER, | ||||
|                 valign: Gtk.Align.START, | ||||
|                 vexpand: true | ||||
|                 vexpand: true, | ||||
|             }); | ||||
|             this.add(button); | ||||
|  | ||||
| @@ -527,13 +518,13 @@ class NoShellPlaceholder extends Gtk.Box { | ||||
|             orientation: Gtk.Orientation.VERTICAL, | ||||
|             spacing: 12, | ||||
|             margin: 100, | ||||
|             margin_bottom: 60 | ||||
|             margin_bottom: 60, | ||||
|         }); | ||||
|  | ||||
|         let label = new Gtk.Label({ | ||||
|             label: '<span size="x-large">%s</span>'.format( | ||||
|                 _("Something’s gone wrong")), | ||||
|             use_markup: true | ||||
|             use_markup: true, | ||||
|         }); | ||||
|         label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); | ||||
|         this.add(label); | ||||
| @@ -541,7 +532,7 @@ class NoShellPlaceholder extends Gtk.Box { | ||||
|         label = new Gtk.Label({ | ||||
|             label: _("We’re very sorry, but it was not possible to get the list of installed extensions. Make sure you are logged into GNOME and try again."), | ||||
|             justify: Gtk.Justification.CENTER, | ||||
|             wrap: true | ||||
|             wrap: true, | ||||
|         }); | ||||
|         this.add(label); | ||||
|  | ||||
| @@ -578,11 +569,11 @@ class ExtensionRow extends Gtk.ListBoxRow { | ||||
|                     return; | ||||
|  | ||||
|                 this._extension = ExtensionUtils.deserializeExtension(newState); | ||||
|                 let state = (this._extension.state == ExtensionState.ENABLED); | ||||
|                 let state = this._extension.state == ExtensionState.ENABLED; | ||||
|  | ||||
|                 GObject.signal_handler_block(this._switch, this._notifyActiveId); | ||||
|                 this._switch.block_signal_handler(this._notifyActiveId); | ||||
|                 this._switch.state = state; | ||||
|                 GObject.signal_handler_unblock(this._switch, this._notifyActiveId); | ||||
|                 this._switch.unblock_signal_handler(this._notifyActiveId); | ||||
|  | ||||
|                 this._switch.sensitive = this._canToggle(); | ||||
|             }); | ||||
| @@ -649,7 +640,7 @@ class ExtensionRow extends Gtk.ListBoxRow { | ||||
|         this._switch = new Gtk.Switch({ | ||||
|             valign: Gtk.Align.CENTER, | ||||
|             sensitive: this._canToggle(), | ||||
|             state: this._extension.state === ExtensionState.ENABLED | ||||
|             state: this._extension.state === ExtensionState.ENABLED, | ||||
|         }); | ||||
|         this._notifyActiveId = this._switch.connect('notify::active', () => { | ||||
|             if (this._switch.active) | ||||
| @@ -666,12 +657,12 @@ class ExtensionRow extends Gtk.ListBoxRow { | ||||
|     } | ||||
|  | ||||
|     get prefsModule() { | ||||
|         // give extension prefs access to their own extension object | ||||
|         ExtensionUtils.getCurrentExtension = () => this._extension; | ||||
|  | ||||
|         if (!this._prefsModule) { | ||||
|             ExtensionUtils.installImporter(this._extension); | ||||
|  | ||||
|             // give extension prefs access to their own extension object | ||||
|             ExtensionUtils.getCurrentExtension = () => this._extension; | ||||
|  | ||||
|             this._prefsModule = this._extension.imports.prefs; | ||||
|             this._prefsModule.init(this._extension.metadata); | ||||
|         } | ||||
| @@ -692,7 +683,7 @@ function initEnvironment() { | ||||
|             log(`ERROR: ${s}`); | ||||
|         }, | ||||
|  | ||||
|         userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']) | ||||
|         userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']), | ||||
|     }; | ||||
|  | ||||
|     String.prototype.format = Format.format; | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported AuthPrompt */ | ||||
|  | ||||
| const { Clutter, Pango, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
| const { Clutter, GObject, Pango, Shell, St } = imports.gi; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const Batch = imports.gdm.batch; | ||||
| const GdmUtil = imports.gdm.util; | ||||
| const Util = imports.misc.util; | ||||
| const Params = imports.misc.params; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const UserWidget = imports.ui.userWidget; | ||||
| @@ -16,25 +17,42 @@ var DEFAULT_BUTTON_WELL_ANIMATION_TIME = 300; | ||||
|  | ||||
| var MESSAGE_FADE_OUT_ANIMATION_TIME = 500; | ||||
|  | ||||
| const WIGGLE_OFFSET = 6; | ||||
| const WIGGLE_DURATION = 65; | ||||
| const N_WIGGLES = 3; | ||||
|  | ||||
| var AuthPromptMode = { | ||||
|     UNLOCK_ONLY: 0, | ||||
|     UNLOCK_OR_LOG_IN: 1 | ||||
|     UNLOCK_OR_LOG_IN: 1, | ||||
| }; | ||||
|  | ||||
| var AuthPromptStatus = { | ||||
|     NOT_VERIFYING: 0, | ||||
|     VERIFYING: 1, | ||||
|     VERIFICATION_FAILED: 2, | ||||
|     VERIFICATION_SUCCEEDED: 3 | ||||
|     VERIFICATION_SUCCEEDED: 3, | ||||
| }; | ||||
|  | ||||
| var BeginRequestType = { | ||||
|     PROVIDE_USERNAME: 0, | ||||
|     DONT_PROVIDE_USERNAME: 1 | ||||
|     DONT_PROVIDE_USERNAME: 1, | ||||
| }; | ||||
|  | ||||
| var AuthPrompt = class { | ||||
|     constructor(gdmClient, mode) { | ||||
| var AuthPrompt = GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'cancelled': {}, | ||||
|         'failed': {}, | ||||
|         'next': {}, | ||||
|         'prompted': {}, | ||||
|         'reset': { param_types: [GObject.TYPE_UINT] }, | ||||
|     }, | ||||
| }, class AuthPrompt extends St.BoxLayout { | ||||
|     _init(gdmClient, mode) { | ||||
|         super._init({ | ||||
|             style_class: 'login-dialog-prompt-layout', | ||||
|             vertical: true, | ||||
|         }); | ||||
|  | ||||
|         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|  | ||||
|         this._gdmClient = gdmClient; | ||||
| @@ -46,7 +64,7 @@ var AuthPrompt = class { | ||||
|         else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN) | ||||
|             reauthenticationOnly = false; | ||||
|  | ||||
|         this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly }); | ||||
|         this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly }); | ||||
|  | ||||
|         this._userVerifier.connect('ask-question', this._onAskQuestion.bind(this)); | ||||
|         this._userVerifier.connect('show-message', this._onShowMessage.bind(this)); | ||||
| @@ -60,70 +78,68 @@ var AuthPrompt = class { | ||||
|         this.connect('next', () => { | ||||
|             this.updateSensitivity(false); | ||||
|             this.startSpinning(); | ||||
|             if (this._queryingService) { | ||||
|             if (this._queryingService) | ||||
|                 this._userVerifier.answerQuery(this._queryingService, this._entry.text); | ||||
|             } else { | ||||
|             else | ||||
|                 this._preemptiveAnswer = this._entry.text; | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout', | ||||
|                                         vertical: true }); | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this.actor.connect('key-press-event', (actor, event) => { | ||||
|             if (event.get_key_symbol() == Clutter.KEY_Escape) | ||||
|                 this.cancel(); | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|  | ||||
|         this._userWell = new St.Bin({ | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this.add_child(this._userWell); | ||||
|         this._label = new St.Label({ | ||||
|             style_class: 'login-dialog-prompt-label', | ||||
|             x_expand: false, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|  | ||||
|         this._userWell = new St.Bin({ x_fill: true, | ||||
|                                       x_align: St.Align.START }); | ||||
|         this.actor.add(this._userWell, | ||||
|                        { x_align: St.Align.START, | ||||
|                          x_fill: true, | ||||
|                          y_fill: true, | ||||
|                          expand: true }); | ||||
|         this._label = new St.Label({ style_class: 'login-dialog-prompt-label' }); | ||||
|  | ||||
|         this.actor.add(this._label, | ||||
|                        { expand: true, | ||||
|                          x_fill: false, | ||||
|                          y_fill: true, | ||||
|                          x_align: St.Align.START }); | ||||
|         this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry', | ||||
|                                      can_focus: true }); | ||||
|         this.add_child(this._label); | ||||
|         this._entry = new St.Entry({ | ||||
|             style_class: 'login-dialog-prompt-entry', | ||||
|             can_focus: true, | ||||
|             x_expand: false, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         ShellEntry.addContextMenu(this._entry, { isPassword: true, actionMode: Shell.ActionMode.NONE }); | ||||
|  | ||||
|         this.actor.add(this._entry, | ||||
|                        { expand: true, | ||||
|                          x_fill: true, | ||||
|                          y_fill: false, | ||||
|                          x_align: St.Align.START }); | ||||
|         this.add_child(this._entry); | ||||
|  | ||||
|         this._entry.grab_key_focus(); | ||||
|  | ||||
|         this._message = new St.Label({ opacity: 0, | ||||
|                                        styleClass: 'login-dialog-message' }); | ||||
|         this._message = new St.Label({ | ||||
|             opacity: 0, | ||||
|             styleClass: 'login-dialog-message', | ||||
|             x_expand: false, | ||||
|             y_expand: true, | ||||
|             y_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|         this._message.clutter_text.line_wrap = true; | ||||
|         this._message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START }); | ||||
|         this.add_child(this._message); | ||||
|  | ||||
|         this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box', | ||||
|                                              vertical: false }); | ||||
|         this.actor.add(this._buttonBox, | ||||
|                        { expand: true, | ||||
|                          x_align: St.Align.MIDDLE, | ||||
|                          y_align: St.Align.END }); | ||||
|         this._buttonBox = new St.BoxLayout({ | ||||
|             style_class: 'login-dialog-button-box', | ||||
|             vertical: false, | ||||
|             y_align: Clutter.ActorAlign.END, | ||||
|         }); | ||||
|         this.add_child(this._buttonBox); | ||||
|  | ||||
|         this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|         this._defaultButtonWellActor = null; | ||||
|         this._defaultButtonWell = new St.Widget({ | ||||
|             layout_manager: new Clutter.BinLayout(), | ||||
|             x_align: Clutter.ActorAlign.END, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|  | ||||
|         this._initButtons(); | ||||
|  | ||||
|         this._spinner = new Animation.Spinner(DEFAULT_BUTTON_WELL_ICON_SIZE); | ||||
|         this._spinner.actor.opacity = 0; | ||||
|         this._spinner.actor.show(); | ||||
|         this._defaultButtonWell.add_child(this._spinner.actor); | ||||
|         this._spinner.opacity = 0; | ||||
|         this._spinner.show(); | ||||
|         this._defaultButtonWell.add_child(this._spinner); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
| @@ -131,39 +147,39 @@ var AuthPrompt = class { | ||||
|         this._userVerifier = null; | ||||
|     } | ||||
|  | ||||
|     _initButtons() { | ||||
|         this.cancelButton = new St.Button({ style_class: 'modal-dialog-button button', | ||||
|                                             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                             reactive: true, | ||||
|                                             can_focus: true, | ||||
|                                             label: _("Cancel") }); | ||||
|         this.cancelButton.connect('clicked', () => this.cancel()); | ||||
|         this._buttonBox.add(this.cancelButton, | ||||
|                             { expand: false, | ||||
|                               x_fill: false, | ||||
|                               y_fill: false, | ||||
|                               x_align: St.Align.START, | ||||
|                               y_align: St.Align.END }); | ||||
|     vfunc_key_press_event(keyPressEvent) { | ||||
|         if (keyPressEvent.keyval == Clutter.KEY_Escape) | ||||
|             this.cancel(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|         this._buttonBox.add(this._defaultButtonWell, | ||||
|                             { expand: true, | ||||
|                               x_fill: false, | ||||
|                               y_fill: false, | ||||
|                               x_align: St.Align.END, | ||||
|                               y_align: St.Align.MIDDLE }); | ||||
|         this.nextButton = new St.Button({ style_class: 'modal-dialog-button button', | ||||
|                                           button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                           reactive: true, | ||||
|                                           can_focus: true, | ||||
|                                           label: _("Next") }); | ||||
|     _initButtons() { | ||||
|         this.cancelButton = new St.Button({ | ||||
|             style_class: 'modal-dialog-button button', | ||||
|             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|             reactive: true, | ||||
|             can_focus: true, | ||||
|             label: _("Cancel"), | ||||
|             x_expand: true, | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|             y_align: Clutter.ActorAlign.END, | ||||
|         }); | ||||
|         this.cancelButton.connect('clicked', () => this.cancel()); | ||||
|         this._buttonBox.add_child(this.cancelButton); | ||||
|  | ||||
|         this._buttonBox.add_child(this._defaultButtonWell); | ||||
|         this.nextButton = new St.Button({ | ||||
|             style_class: 'modal-dialog-button button', | ||||
|             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|             reactive: true, | ||||
|             can_focus: true, | ||||
|             label: _("Next"), | ||||
|             x_align: Clutter.ActorAlign.END, | ||||
|             y_align: Clutter.ActorAlign.END, | ||||
|         }); | ||||
|         this.nextButton.connect('clicked', () => this.emit('next')); | ||||
|         this.nextButton.add_style_pseudo_class('default'); | ||||
|         this._buttonBox.add(this.nextButton, | ||||
|                             { expand: false, | ||||
|                               x_fill: false, | ||||
|                               y_fill: false, | ||||
|                               x_align: St.Align.END, | ||||
|                               y_align: St.Align.END }); | ||||
|         this._buttonBox.add_child(this.nextButton); | ||||
|  | ||||
|         this._updateNextButtonSensitivity(this._entry.text.length > 0); | ||||
|  | ||||
| @@ -241,6 +257,12 @@ var AuthPrompt = class { | ||||
|         this.updateSensitivity(canRetry); | ||||
|         this.setActorInDefaultButtonWell(null); | ||||
|         this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED; | ||||
|  | ||||
|         Util.wiggle(this._entry, { | ||||
|             offset: WIGGLE_OFFSET, | ||||
|             duration: WIGGLE_DURATION, | ||||
|             wiggleCount: N_WIGGLES, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _onVerificationComplete() { | ||||
| @@ -269,13 +291,13 @@ var AuthPrompt = class { | ||||
|             oldActor.remove_all_transitions(); | ||||
|  | ||||
|         let wasSpinner; | ||||
|         if (oldActor == this._spinner.actor) | ||||
|         if (oldActor == this._spinner) | ||||
|             wasSpinner = true; | ||||
|         else | ||||
|             wasSpinner = false; | ||||
|  | ||||
|         let isSpinner; | ||||
|         if (actor == this._spinner.actor) | ||||
|         if (actor == this._spinner) | ||||
|             isSpinner = true; | ||||
|         else | ||||
|             isSpinner = false; | ||||
| @@ -299,7 +321,7 @@ var AuthPrompt = class { | ||||
|                             if (this._spinner) | ||||
|                                 this._spinner.stop(); | ||||
|                         } | ||||
|                     } | ||||
|                     }, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
| @@ -308,22 +330,23 @@ var AuthPrompt = class { | ||||
|             if (isSpinner) | ||||
|                 this._spinner.play(); | ||||
|  | ||||
|             if (!animate) | ||||
|             if (!animate) { | ||||
|                 actor.opacity = 255; | ||||
|             else | ||||
|             } else { | ||||
|                 actor.ease({ | ||||
|                     opacity: 255, | ||||
|                     duration: DEFAULT_BUTTON_WELL_ANIMATION_TIME, | ||||
|                     delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY, | ||||
|                     mode: Clutter.AnimationMode.LINEAR | ||||
|                     mode: Clutter.AnimationMode.LINEAR, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this._defaultButtonWellActor = actor; | ||||
|     } | ||||
|  | ||||
|     startSpinning() { | ||||
|         this.setActorInDefaultButtonWell(this._spinner.actor, true); | ||||
|         this.setActorInDefaultButtonWell(this._spinner, true); | ||||
|     } | ||||
|  | ||||
|     stopSpinning() { | ||||
| @@ -369,7 +392,7 @@ var AuthPrompt = class { | ||||
|         this._message.ease({ | ||||
|             opacity: 0, | ||||
|             duration: MESSAGE_FADE_OUT_ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -404,9 +427,9 @@ var AuthPrompt = class { | ||||
|         this._entry.clutter_text.editable = sensitive; | ||||
|     } | ||||
|  | ||||
|     hide() { | ||||
|     vfunc_hide() { | ||||
|         this.setActorInDefaultButtonWell(null, true); | ||||
|         this.actor.hide(); | ||||
|         super.vfunc_hide(); | ||||
|         this._message.opacity = 0; | ||||
|  | ||||
|         this.setUser(null); | ||||
| @@ -422,7 +445,8 @@ var AuthPrompt = class { | ||||
|  | ||||
|         if (user) { | ||||
|             let userWidget = new UserWidget.UserWidget(user); | ||||
|             this._userWell.set_child(userWidget.actor); | ||||
|             userWidget.x_align = Clutter.ActorAlign.START; | ||||
|             this._userWell.set_child(userWidget); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -501,11 +525,10 @@ var AuthPrompt = class { | ||||
|     } | ||||
|  | ||||
|     cancel() { | ||||
|         if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|         if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this.reset(); | ||||
|         this.emit('cancelled'); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(AuthPrompt.prototype); | ||||
| }); | ||||
|   | ||||
| @@ -112,13 +112,12 @@ var Batch = class extends Task { | ||||
|         for (let i = 0; i < tasks.length; i++) { | ||||
|             let task; | ||||
|  | ||||
|             if (tasks[i] instanceof Task) { | ||||
|             if (tasks[i] instanceof Task) | ||||
|                 task = tasks[i]; | ||||
|             } else if (typeof tasks[i] == 'function') { | ||||
|             else if (typeof tasks[i] == 'function') | ||||
|                 task = new Task(scope, tasks[i]); | ||||
|             } else { | ||||
|             else | ||||
|                 throw new Error('Batch tasks must be functions or Task, Hold or Batch objects'); | ||||
|             } | ||||
|  | ||||
|             this.tasks.push(task); | ||||
|         } | ||||
| @@ -129,9 +128,8 @@ var Batch = class extends Task { | ||||
|     } | ||||
|  | ||||
|     runTask() { | ||||
|         if (!(this._currentTaskIndex in this.tasks)) { | ||||
|         if (!(this._currentTaskIndex in this.tasks)) | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return this.tasks[this._currentTaskIndex].run(); | ||||
|     } | ||||
| @@ -179,9 +177,8 @@ var ConcurrentBatch = class extends Batch { | ||||
|     process() { | ||||
|         let hold = this.runTask(); | ||||
|  | ||||
|         if (hold) { | ||||
|         if (hold) | ||||
|             this.hold.acquireUntilAfter(hold); | ||||
|         } | ||||
|  | ||||
|         // Regardless of the state of the just run task, | ||||
|         // fire off the next one, so all the tasks can run | ||||
|   | ||||
| @@ -20,7 +20,7 @@ function FprintManager() { | ||||
|                                    g_interface_info: FprintManagerInfo, | ||||
|                                    g_name: 'net.reactivated.Fprint', | ||||
|                                    g_object_path: '/net/reactivated/Fprint/Manager', | ||||
|                                    g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); | ||||
|                                    g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES }); | ||||
|  | ||||
|     try { | ||||
|         self.init(null); | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
|  | ||||
| const { AccountsService, Atk, Clutter, Gdm, Gio, | ||||
|         GLib, GObject, Meta, Pango, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const AuthPrompt = imports.gdm.authPrompt; | ||||
| const Batch = imports.gdm.batch; | ||||
| @@ -39,72 +38,81 @@ const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; | ||||
| const _LOGO_ICON_HEIGHT = 48; | ||||
| const _MAX_BOTTOM_MENU_ITEMS = 5; | ||||
|  | ||||
| var UserListItem = class { | ||||
|     constructor(user) { | ||||
| var UserListItem = GObject.registerClass({ | ||||
|     Signals: { 'activate': {} }, | ||||
| }, class UserListItem extends St.Button { | ||||
|     _init(user) { | ||||
|         let layout = new St.BoxLayout({ | ||||
|             vertical: true, | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|         super._init({ | ||||
|             style_class: 'login-dialog-user-list-item', | ||||
|             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|             can_focus: true, | ||||
|             x_expand: true, | ||||
|             child: layout, | ||||
|             reactive: true, | ||||
|         }); | ||||
|  | ||||
|         this.user = user; | ||||
|         this._userChangedId = this.user.connect('changed', | ||||
|                                                 this._onUserChanged.bind(this)); | ||||
|  | ||||
|         let layout = new St.BoxLayout({ vertical: true }); | ||||
|         this.actor = new St.Button({ style_class: 'login-dialog-user-list-item', | ||||
|                                      button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                      can_focus: true, | ||||
|                                      child: layout, | ||||
|                                      reactive: true, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      x_fill: true }); | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|  | ||||
|         this.actor.connect('key-focus-in', () => { | ||||
|             this._setSelected(true); | ||||
|         }); | ||||
|         this.actor.connect('key-focus-out', () => { | ||||
|             this._setSelected(false); | ||||
|         }); | ||||
|         this.actor.connect('notify::hover', () => { | ||||
|             this._setSelected(this.actor.hover); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this.connect('notify::hover', () => { | ||||
|             this._setSelected(this.hover); | ||||
|         }); | ||||
|  | ||||
|         this._userWidget = new UserWidget.UserWidget(this.user); | ||||
|         layout.add(this._userWidget.actor); | ||||
|         layout.add(this._userWidget); | ||||
|  | ||||
|         this._userWidget.actor.bind_property('label-actor', this.actor, 'label-actor', | ||||
|                                              GObject.BindingFlags.SYNC_CREATE); | ||||
|         this._userWidget.bind_property('label-actor', this, 'label-actor', | ||||
|                                        GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|         this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', | ||||
|                                                  scale_x: 0, | ||||
|                                                  visible: false }); | ||||
|         layout.add(this._timedLoginIndicator); | ||||
|  | ||||
|         this.actor.connect('clicked', this._onClicked.bind(this)); | ||||
|         this._onUserChanged(); | ||||
|     } | ||||
|  | ||||
|     vfunc_key_focus_in() { | ||||
|         super.vfunc_key_focus_in(); | ||||
|         this._setSelected(true); | ||||
|     } | ||||
|  | ||||
|     vfunc_key_focus_out() { | ||||
|         super.vfunc_key_focus_out(); | ||||
|         this._setSelected(false); | ||||
|     } | ||||
|  | ||||
|     _onUserChanged() { | ||||
|         this._updateLoggedIn(); | ||||
|     } | ||||
|  | ||||
|     _updateLoggedIn() { | ||||
|         if (this.user.is_logged_in()) | ||||
|             this.actor.add_style_pseudo_class('logged-in'); | ||||
|             this.add_style_pseudo_class('logged-in'); | ||||
|         else | ||||
|             this.actor.remove_style_pseudo_class('logged-in'); | ||||
|             this.remove_style_pseudo_class('logged-in'); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
|         this.user.disconnect(this._userChangedId); | ||||
|     } | ||||
|  | ||||
|     _onClicked() { | ||||
|     vfunc_clicked() { | ||||
|         this.emit('activate'); | ||||
|     } | ||||
|  | ||||
|     _setSelected(selected) { | ||||
|         if (selected) { | ||||
|             this.actor.add_style_pseudo_class('selected'); | ||||
|             this.actor.grab_key_focus(); | ||||
|             this.add_style_pseudo_class('selected'); | ||||
|             this.grab_key_focus(); | ||||
|         } else { | ||||
|             this.actor.remove_style_pseudo_class('selected'); | ||||
|             this.remove_style_pseudo_class('selected'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -117,7 +125,7 @@ var UserListItem = class { | ||||
|  | ||||
|         let startTime = GLib.get_monotonic_time(); | ||||
|  | ||||
|         this._timedLoginTimeoutId = GLib.timeout_add (GLib.PRIORITY_DEFAULT, 33, | ||||
|         this._timedLoginTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 33, | ||||
|             () => { | ||||
|                 let currentTime = GLib.get_monotonic_time(); | ||||
|                 let elapsedTime = (currentTime - startTime) / GLib.USEC_PER_SEC; | ||||
| @@ -145,23 +153,33 @@ var UserListItem = class { | ||||
|         this._timedLoginIndicator.visible = false; | ||||
|         this._timedLoginIndicator.scale_x = 0.; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(UserListItem.prototype); | ||||
| }); | ||||
|  | ||||
| var UserList = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.ScrollView({ style_class: 'login-dialog-user-list-view' }); | ||||
|         this.actor.set_policy(St.PolicyType.NEVER, | ||||
|                               St.PolicyType.AUTOMATIC); | ||||
| var UserList = GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'activate': { param_types: [UserListItem.$gtype] }, | ||||
|         'item-added': { param_types: [UserListItem.$gtype] }, | ||||
|     }, | ||||
| }, class UserList extends St.ScrollView { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             style_class: 'login-dialog-user-list-view', | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this.set_policy(St.PolicyType.NEVER, | ||||
|                         St.PolicyType.AUTOMATIC); | ||||
|  | ||||
|         this._box = new St.BoxLayout({ vertical: true, | ||||
|                                        style_class: 'login-dialog-user-list', | ||||
|                                        pseudo_class: 'expanded' }); | ||||
|  | ||||
|         this.actor.add_actor(this._box); | ||||
|         this.add_actor(this._box); | ||||
|         this._items = {}; | ||||
|     } | ||||
|  | ||||
|         this.actor.connect('key-focus-in', this._moveFocusToItems.bind(this)); | ||||
|     vfunc_key_focus_in() { | ||||
|         this._moveFocusToItems(); | ||||
|     } | ||||
|  | ||||
|     _moveFocusToItems() { | ||||
| @@ -170,10 +188,10 @@ var UserList = class { | ||||
|         if (!hasItems) | ||||
|             return; | ||||
|  | ||||
|         if (global.stage.get_key_focus() != this.actor) | ||||
|         if (global.stage.get_key_focus() != this) | ||||
|             return; | ||||
|  | ||||
|         let focusSet = this.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false); | ||||
|         let focusSet = this.navigate_focus(null, St.DirectionType.TAB_FORWARD, false); | ||||
|         if (!focusSet) { | ||||
|             Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { | ||||
|                 this._moveFocusToItems(); | ||||
| @@ -194,26 +212,26 @@ var UserList = class { | ||||
|  | ||||
|         for (let userName in this._items) { | ||||
|             let item = this._items[userName]; | ||||
|             item.actor.sync_hover(); | ||||
|             item.sync_hover(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     scrollToItem(item) { | ||||
|         let box = item.actor.get_allocation_box(); | ||||
|         let box = item.get_allocation_box(); | ||||
|  | ||||
|         let adjustment = this.actor.get_vscroll_bar().get_adjustment(); | ||||
|         let adjustment = this.get_vscroll_bar().get_adjustment(); | ||||
|  | ||||
|         let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0); | ||||
|         adjustment.ease(value, { | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             duration: _SCROLL_ANIMATION_TIME | ||||
|             duration: _SCROLL_ANIMATION_TIME, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     jumpToItem(item) { | ||||
|         let box = item.actor.get_allocation_box(); | ||||
|         let box = item.get_allocation_box(); | ||||
|  | ||||
|         let adjustment = this.actor.get_vscroll_bar().get_adjustment(); | ||||
|         let adjustment = this.get_vscroll_bar().get_adjustment(); | ||||
|  | ||||
|         let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0); | ||||
|  | ||||
| @@ -251,14 +269,14 @@ var UserList = class { | ||||
|         this.removeUser(user); | ||||
|  | ||||
|         let item = new UserListItem(user); | ||||
|         this._box.add(item.actor, { x_fill: true }); | ||||
|         this._box.add_child(item); | ||||
|  | ||||
|         this._items[userName] = item; | ||||
|  | ||||
|         item.connect('activate', this._onItemActivated.bind(this)); | ||||
|  | ||||
|         // Try to keep the focused item front-and-center | ||||
|         item.actor.connect('key-focus-in', () => this.scrollToItem(item)); | ||||
|         item.connect('key-focus-in', () => this.scrollToItem(item)); | ||||
|  | ||||
|         this._moveFocusToItems(); | ||||
|  | ||||
| @@ -279,33 +297,37 @@ var UserList = class { | ||||
|         if (!item) | ||||
|             return; | ||||
|  | ||||
|         item.actor.destroy(); | ||||
|         item.destroy(); | ||||
|         delete this._items[userName]; | ||||
|     } | ||||
|  | ||||
|     numItems() { | ||||
|         return Object.keys(this._items).length; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(UserList.prototype); | ||||
| }); | ||||
|  | ||||
| var SessionMenuButton = class { | ||||
|     constructor() { | ||||
| var SessionMenuButton = GObject.registerClass({ | ||||
|     Signals: { 'session-activated': { param_types: [GObject.TYPE_STRING] } }, | ||||
| }, class SessionMenuButton extends St.Bin { | ||||
|     _init() { | ||||
|         let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' }); | ||||
|         this._button = new St.Button({ style_class: 'login-dialog-session-list-button', | ||||
|                                        reactive: true, | ||||
|                                        track_hover: true, | ||||
|                                        can_focus: true, | ||||
|                                        accessible_name: _("Choose Session"), | ||||
|                                        accessible_role: Atk.Role.MENU, | ||||
|                                        child: gearIcon }); | ||||
|         let button = new St.Button({ | ||||
|             style_class: 'login-dialog-session-list-button', | ||||
|             reactive: true, | ||||
|             track_hover: true, | ||||
|             can_focus: true, | ||||
|             accessible_name: _("Choose Session"), | ||||
|             accessible_role: Atk.Role.MENU, | ||||
|             child: gearIcon, | ||||
|         }); | ||||
|  | ||||
|         this.actor = new St.Bin({ child: this._button }); | ||||
|         super._init({ child: button }); | ||||
|         this._button = button; | ||||
|  | ||||
|         let side = St.Side.TOP; | ||||
|         let align = 0; | ||||
|         if (Gdm.get_session_ids().length > _MAX_BOTTOM_MENU_ITEMS) { | ||||
|             if (this.actor.text_direction == Clutter.TextDirection.RTL) | ||||
|             if (this.text_direction == Clutter.TextDirection.RTL) | ||||
|                 side = St.Side.RIGHT; | ||||
|             else | ||||
|                 side = St.Side.LEFT; | ||||
| @@ -384,15 +406,13 @@ var SessionMenuButton = class { | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(SessionMenuButton.prototype); | ||||
| }); | ||||
|  | ||||
| var LoginDialog = GObject.registerClass({ | ||||
|     Signals: { 'failed': {} }, | ||||
| }, class LoginDialog extends St.Widget { | ||||
|     _init(parentActor) { | ||||
|         super._init({ style_class: 'login-dialog', | ||||
|                       visible: false }); | ||||
|         super._init({ style_class: 'login-dialog', visible: false }); | ||||
|  | ||||
|         this.get_accessible().set_role(Atk.Role.WINDOW); | ||||
|  | ||||
| @@ -426,38 +446,35 @@ var LoginDialog = GObject.registerClass({ | ||||
|         this.add_child(this._userSelectionBox); | ||||
|  | ||||
|         this._userList = new UserList(); | ||||
|         this._userSelectionBox.add(this._userList.actor, | ||||
|                                    { expand: true, | ||||
|                                      x_fill: true, | ||||
|                                      y_fill: true }); | ||||
|         this._userSelectionBox.add_child(this._userList); | ||||
|  | ||||
|         this._authPrompt = new AuthPrompt.AuthPrompt(this._gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN); | ||||
|         this._authPrompt.connect('prompted', this._onPrompted.bind(this)); | ||||
|         this._authPrompt.connect('reset', this._onReset.bind(this)); | ||||
|         this._authPrompt.hide(); | ||||
|         this.add_child(this._authPrompt.actor); | ||||
|         this.add_child(this._authPrompt); | ||||
|  | ||||
|         // translators: this message is shown below the user list on the | ||||
|         // login screen. It can be activated to reveal an entry for | ||||
|         // manually entering the username. | ||||
|         let notListedLabel = new St.Label({ text: _("Not listed?"), | ||||
|                                             style_class: 'login-dialog-not-listed-label' }); | ||||
|         this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button', | ||||
|                                                 button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                                 can_focus: true, | ||||
|                                                 child: notListedLabel, | ||||
|                                                 reactive: true, | ||||
|                                                 x_align: St.Align.START, | ||||
|                                                 x_fill: true }); | ||||
|         let notListedLabel = new St.Label({ | ||||
|             text: _("Not listed?"), | ||||
|             style_class: 'login-dialog-not-listed-label', | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|         this._notListedButton = new St.Button({ | ||||
|             style_class: 'login-dialog-not-listed-button', | ||||
|             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|             can_focus: true, | ||||
|             child: notListedLabel, | ||||
|             reactive: true, | ||||
|         }); | ||||
|  | ||||
|         this._notListedButton.connect('clicked', this._hideUserListAskForUsernameAndBeginVerification.bind(this)); | ||||
|  | ||||
|         this._notListedButton.hide(); | ||||
|  | ||||
|         this._userSelectionBox.add(this._notListedButton, | ||||
|                                    { expand: false, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      x_fill: true }); | ||||
|         this._userSelectionBox.add_child(this._notListedButton); | ||||
|  | ||||
|         this._bannerView = new St.ScrollView({ style_class: 'login-dialog-banner-view', | ||||
|                                                opacity: 0, | ||||
| @@ -492,11 +509,11 @@ var LoginDialog = GObject.registerClass({ | ||||
|         this._sessionMenuButton = new SessionMenuButton(); | ||||
|         this._sessionMenuButton.connect('session-activated', | ||||
|             (list, sessionId) => { | ||||
|                 this._greeter.call_select_session_sync (sessionId, null); | ||||
|                 this._greeter.call_select_session_sync(sessionId, null); | ||||
|             }); | ||||
|         this._sessionMenuButton.actor.opacity = 0; | ||||
|         this._sessionMenuButton.actor.show(); | ||||
|         this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor); | ||||
|         this._sessionMenuButton.opacity = 0; | ||||
|         this._sessionMenuButton.show(); | ||||
|         this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton); | ||||
|  | ||||
|         this._disableUserList = undefined; | ||||
|         this._userListLoaded = false; | ||||
| @@ -579,8 +596,8 @@ var LoginDialog = GObject.registerClass({ | ||||
|  | ||||
|         let authPromptAllocation = null; | ||||
|         let authPromptWidth = 0; | ||||
|         if (this._authPrompt.actor.visible) { | ||||
|             authPromptAllocation = this._getCenterActorAllocation(dialogBox, this._authPrompt.actor); | ||||
|         if (this._authPrompt.visible) { | ||||
|             authPromptAllocation = this._getCenterActorAllocation(dialogBox, this._authPrompt); | ||||
|             authPromptWidth = authPromptAllocation.x2 - authPromptAllocation.x1; | ||||
|         } | ||||
|  | ||||
| @@ -685,12 +702,11 @@ var LoginDialog = GObject.registerClass({ | ||||
|         } | ||||
|  | ||||
|         // Finally hand out the allocations | ||||
|         if (bannerAllocation) { | ||||
|         if (bannerAllocation) | ||||
|             this._bannerView.allocate(bannerAllocation, flags); | ||||
|         } | ||||
|  | ||||
|         if (authPromptAllocation) | ||||
|             this._authPrompt.actor.allocate(authPromptAllocation, flags); | ||||
|             this._authPrompt.allocate(authPromptAllocation, flags); | ||||
|  | ||||
|         if (userSelectionAllocation) | ||||
|             this._userSelectionBox.allocate(userSelectionAllocation, flags); | ||||
| @@ -760,7 +776,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|         this._bannerView.ease({ | ||||
|             opacity: 255, | ||||
|             duration: _FADE_ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -794,7 +810,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|     _onPrompted() { | ||||
|         if (this._shouldShowSessionMenuButton()) { | ||||
|             this._sessionMenuButton.updateSensitivity(true); | ||||
|             this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor); | ||||
|             this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton); | ||||
|         } else { | ||||
|             this._sessionMenuButton.updateSensitivity(false); | ||||
|         } | ||||
| @@ -803,9 +819,9 @@ var LoginDialog = GObject.registerClass({ | ||||
|  | ||||
|     _resetGreeterProxy() { | ||||
|         if (GLib.getenv('GDM_GREETER_TEST') != '1') { | ||||
|             if (this._greeter) { | ||||
|             if (this._greeter) | ||||
|                 this._greeter.run_dispose(); | ||||
|             } | ||||
|  | ||||
|             this._greeter = this._gdmClient.get_greeter_sync(null); | ||||
|  | ||||
|             this._defaultSessionChangedId = this._greeter.connect('default-session-name-changed', | ||||
| @@ -854,14 +870,14 @@ var LoginDialog = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     _showPrompt() { | ||||
|         if (this._authPrompt.actor.visible) | ||||
|         if (this._authPrompt.visible) | ||||
|             return; | ||||
|         this._authPrompt.actor.opacity = 0; | ||||
|         this._authPrompt.actor.show(); | ||||
|         this._authPrompt.actor.ease({ | ||||
|         this._authPrompt.opacity = 0; | ||||
|         this._authPrompt.show(); | ||||
|         this._authPrompt.ease({ | ||||
|             opacity: 255, | ||||
|             duration: _FADE_ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|         this._fadeInBannerView(); | ||||
|     } | ||||
| @@ -929,7 +945,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|                 if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING) | ||||
|                     this._authPrompt.reset(); | ||||
|                 this._unbindOpacity(); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -951,7 +967,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|             onComplete: () => { | ||||
|                 this._greeter.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|                 this._unbindOpacity(); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -968,7 +984,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|         let hold = new Batch.Hold(); | ||||
|         let signalId = this._userList.connect('item-added', | ||||
|             () => { | ||||
|                 let item = this._userList.getItemFromUserName(userName); | ||||
|                 item = this._userList.getItemFromUserName(userName); | ||||
|  | ||||
|                 if (item) | ||||
|                     hold.release(); | ||||
| @@ -1022,9 +1038,8 @@ var LoginDialog = GObject.registerClass({ | ||||
|  | ||||
|                      () => { | ||||
|                          // If we're just starting out, start on the right item. | ||||
|                          if (!this._userManager.is_loaded) { | ||||
|                          if (!this._userManager.is_loaded) | ||||
|                              this._userList.jumpToItem(loginItem); | ||||
|                          } | ||||
|                      }, | ||||
|  | ||||
|                      () => { | ||||
| @@ -1045,12 +1060,12 @@ var LoginDialog = GObject.registerClass({ | ||||
|                      () => { | ||||
|                          // If idle timeout is done, make sure the timed login indicator is shown | ||||
|                          if (delay > _TIMED_LOGIN_IDLE_THRESHOLD && | ||||
|                              this._authPrompt.actor.visible) | ||||
|                              this._authPrompt.visible) | ||||
|                              this._authPrompt.cancel(); | ||||
|  | ||||
|                          if (delay > _TIMED_LOGIN_IDLE_THRESHOLD || firstRun) { | ||||
|                              this._userList.scrollToItem(loginItem); | ||||
|                              loginItem.actor.grab_key_focus(); | ||||
|                              loginItem.grab_key_focus(); | ||||
|                          } | ||||
|                      }, | ||||
|  | ||||
| @@ -1075,9 +1090,8 @@ var LoginDialog = GObject.registerClass({ | ||||
|         // Restart timed login on user interaction | ||||
|         global.stage.connect('captured-event', (actor, event) => { | ||||
|             if (event.type() == Clutter.EventType.KEY_PRESS || | ||||
|                event.type() == Clutter.EventType.BUTTON_PRESS) { | ||||
|                 event.type() == Clutter.EventType.BUTTON_PRESS) | ||||
|                 this._startTimedLogin(userName, seconds); | ||||
|             } | ||||
|  | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         }); | ||||
| @@ -1111,7 +1125,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|         this._sessionMenuButton.close(); | ||||
|         this._setUserListExpanded(true); | ||||
|         this._notListedButton.show(); | ||||
|         this._userList.actor.grab_key_focus(); | ||||
|         this._userList.grab_key_focus(); | ||||
|     } | ||||
|  | ||||
|     _beginVerificationForItem(item) { | ||||
| @@ -1120,8 +1134,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|         let userName = item.user.get_user_name(); | ||||
|         let hold = new Batch.Hold(); | ||||
|  | ||||
|         this._authPrompt.begin({ userName: userName, | ||||
|                                  hold: hold }); | ||||
|         this._authPrompt.begin({ userName, hold }); | ||||
|         return hold; | ||||
|     } | ||||
|  | ||||
| @@ -1184,9 +1197,8 @@ var LoginDialog = GObject.registerClass({ | ||||
|  | ||||
|         let users = this._userManager.list_users(); | ||||
|  | ||||
|         for (let i = 0; i < users.length; i++) { | ||||
|         for (let i = 0; i < users.length; i++) | ||||
|             this._userList.addUser(users[i]); | ||||
|         } | ||||
|  | ||||
|         this._updateDisableUserList(); | ||||
|  | ||||
| @@ -1219,7 +1231,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|                                         _("Login Window"), | ||||
|                                         'dialog-password-symbolic', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.MIDDLE }); | ||||
|         this._userList.actor.grab_key_focus(); | ||||
|         this._userList.grab_key_focus(); | ||||
|         this.show(); | ||||
|         this.opacity = 0; | ||||
|  | ||||
| @@ -1228,7 +1240,7 @@ var LoginDialog = GObject.registerClass({ | ||||
|         this.ease({ | ||||
|             opacity: 255, | ||||
|             duration: 1000, | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|         }); | ||||
|  | ||||
|         return true; | ||||
|   | ||||
| @@ -23,7 +23,7 @@ function OVirtCredentials() { | ||||
|                                    g_interface_info: OVirtCredentialsInfo, | ||||
|                                    g_name: 'org.ovirt.vdsm.Credentials', | ||||
|                                    g_object_path: '/org/ovirt/vdsm/Credentials', | ||||
|                                    g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); | ||||
|                                    g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES }); | ||||
|     self.init(null); | ||||
|     return self; | ||||
| } | ||||
|   | ||||
| @@ -37,7 +37,7 @@ var MessageType = { | ||||
|     NONE: 0, | ||||
|     ERROR: 1, | ||||
|     INFO: 2, | ||||
|     HINT: 3 | ||||
|     HINT: 3, | ||||
| }; | ||||
|  | ||||
| function fadeInActor(actor) { | ||||
| @@ -58,7 +58,7 @@ function fadeInActor(actor) { | ||||
|         onComplete: () => { | ||||
|             this.set_height(-1); | ||||
|             hold.release(); | ||||
|         } | ||||
|         }, | ||||
|     }); | ||||
|  | ||||
|     return hold; | ||||
| @@ -81,7 +81,7 @@ function fadeOutActor(actor) { | ||||
|             this.hide(); | ||||
|             this.set_height(-1); | ||||
|             hold.release(); | ||||
|         } | ||||
|         }, | ||||
|     }); | ||||
|     return hold; | ||||
| } | ||||
| @@ -109,7 +109,7 @@ function cloneAndFadeOutActor(actor) { | ||||
|         onComplete: () => { | ||||
|             clone.destroy(); | ||||
|             hold.release(); | ||||
|         } | ||||
|         }, | ||||
|     }); | ||||
|     return hold; | ||||
| } | ||||
| @@ -272,7 +272,7 @@ var ShellUserVerifier = class { | ||||
|         let interval = this._getIntervalForMessage(message); | ||||
|  | ||||
|         this.hasPendingMessages = true; | ||||
|         this._messageQueue.push({ text: message, type: messageType, interval: interval }); | ||||
|         this._messageQueue.push({ text: message, type: messageType, interval }); | ||||
|         this._queueMessageTimeout(); | ||||
|     } | ||||
|  | ||||
| @@ -544,6 +544,7 @@ var ShellUserVerifier = class { | ||||
|                 }); | ||||
|             } | ||||
|         } else { | ||||
|             // eslint-disable-next-line no-lonely-if | ||||
|             if (!this.hasPendingMessages) { | ||||
|                 this._cancelAndReset(); | ||||
|             } else { | ||||
| @@ -571,9 +572,8 @@ var ShellUserVerifier = class { | ||||
|         // if the password service fails, then cancel everything. | ||||
|         // But if, e.g., fingerprint fails, still give | ||||
|         // password authentication a chance to succeed | ||||
|         if (this.serviceIsForeground(serviceName)) { | ||||
|         if (this.serviceIsForeground(serviceName)) | ||||
|             this._verificationFailed(true); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(ShellUserVerifier.prototype); | ||||
|   | ||||
| @@ -15,7 +15,7 @@ const Config = imports.misc.config; | ||||
|  | ||||
| var ExtensionType = { | ||||
|     SYSTEM: 1, | ||||
|     PER_USER: 2 | ||||
|     PER_USER: 2, | ||||
| }; | ||||
|  | ||||
| var ExtensionState = { | ||||
| @@ -28,7 +28,7 @@ var ExtensionState = { | ||||
|  | ||||
|     // Used as an error state for operations on unknown extensions, | ||||
|     // should never be in a real extensionMeta object. | ||||
|     UNINSTALLED: 99 | ||||
|     UNINSTALLED: 99, | ||||
| }; | ||||
|  | ||||
| const SERIALIZED_PROPERTIES = ['type', 'state', 'path', 'error', 'hasPrefs', 'canChange']; | ||||
| @@ -36,10 +36,11 @@ const SERIALIZED_PROPERTIES = ['type', 'state', 'path', 'error', 'hasPrefs', 'ca | ||||
| /** | ||||
|  * getCurrentExtension: | ||||
|  * | ||||
|  * Returns the current extension, or null if not called from an extension. | ||||
|  * @returns {?object} - The current extension, or null if not called from | ||||
|  * an extension. | ||||
|  */ | ||||
| function getCurrentExtension() { | ||||
|     let stack = (new Error()).stack.split('\n'); | ||||
|     let stack = new Error().stack.split('\n'); | ||||
|     let extensionStackLine; | ||||
|  | ||||
|     // Search for an occurrence of an extension stack frame | ||||
| @@ -84,7 +85,7 @@ function getCurrentExtension() { | ||||
|  | ||||
| /** | ||||
|  * initTranslations: | ||||
|  * @domain: (optional): the gettext domain to use | ||||
|  * @param {string=} domain - the gettext domain to use | ||||
|  * | ||||
|  * Initialize Gettext to load translations from extensionsdir/locale. | ||||
|  * If @domain is not provided, it will be taken from metadata['gettext-domain'] | ||||
| @@ -108,7 +109,8 @@ function initTranslations(domain) { | ||||
|  | ||||
| /** | ||||
|  * getSettings: | ||||
|  * @schema: (optional): the GSettings schema id | ||||
|  * @param {string=} schema - the GSettings schema id | ||||
|  * @returns {Gio.Settings} - a new settings object for @schema | ||||
|  * | ||||
|  * Builds and returns a GSettings schema for @schema, using schema files | ||||
|  * in extensionsdir/schemas. If @schema is omitted, it is taken from | ||||
| @@ -128,12 +130,13 @@ function getSettings(schema) { | ||||
|     // SYSTEM extension that has been installed in the same prefix as the shell | ||||
|     let schemaDir = extension.dir.get_child('schemas'); | ||||
|     let schemaSource; | ||||
|     if (schemaDir.query_exists(null)) | ||||
|     if (schemaDir.query_exists(null)) { | ||||
|         schemaSource = GioSSS.new_from_directory(schemaDir.get_path(), | ||||
|                                                  GioSSS.get_default(), | ||||
|                                                  false); | ||||
|     else | ||||
|     } else { | ||||
|         schemaSource = GioSSS.get_default(); | ||||
|     } | ||||
|  | ||||
|     let schemaObj = schemaSource.lookup(schema, true); | ||||
|     if (!schemaObj) | ||||
| @@ -144,8 +147,9 @@ function getSettings(schema) { | ||||
|  | ||||
| /** | ||||
|  * versionCheck: | ||||
|  * @required: an array of versions we're compatible with | ||||
|  * @current: the version we have | ||||
|  * @param {string[]} required - an array of versions we're compatible with | ||||
|  * @param {string} current - the version we have | ||||
|  * @returns {bool} - true if @current is compatible with @required | ||||
|  * | ||||
|  * Check if a component is compatible for an extension. | ||||
|  * @required is an array, and at least one version must match. | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported collectFromDatadirs, deleteGFile, recursivelyDeleteDir, | ||||
| /* exported collectFromDatadirs, recursivelyDeleteDir, | ||||
|             recursivelyMoveDir, loadInterfaceXML */ | ||||
|  | ||||
| const { Gio, GLib } = imports.gi; | ||||
| @@ -29,11 +29,6 @@ function collectFromDatadirs(subdir, includeUserDir, processFile) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function deleteGFile(file) { | ||||
|     // Work around 'delete' being a keyword in JS. | ||||
|     return file['delete'](null); | ||||
| } | ||||
|  | ||||
| function recursivelyDeleteDir(dir, deleteParent) { | ||||
|     let children = dir.enumerate_children('standard::name,standard::type', | ||||
|                                           Gio.FileQueryInfoFlags.NONE, null); | ||||
| @@ -43,13 +38,13 @@ function recursivelyDeleteDir(dir, deleteParent) { | ||||
|         let type = info.get_file_type(); | ||||
|         let child = dir.get_child(info.get_name()); | ||||
|         if (type == Gio.FileType.REGULAR) | ||||
|             deleteGFile(child); | ||||
|             child.delete(null); | ||||
|         else if (type == Gio.FileType.DIRECTORY) | ||||
|             recursivelyDeleteDir(child, true); | ||||
|     } | ||||
|  | ||||
|     if (deleteParent) | ||||
|         deleteGFile(dir); | ||||
|         dir.delete(null); | ||||
| } | ||||
|  | ||||
| function recursivelyMoveDir(srcDir, destDir) { | ||||
| @@ -75,14 +70,14 @@ let _ifaceResource = null; | ||||
| function loadInterfaceXML(iface) { | ||||
|     if (!_ifaceResource) { | ||||
|         // don't use global.datadir so the method is usable from tests/tools | ||||
|         let dir = GLib.getenv ('GNOME_SHELL_DATADIR') || Config.PKGDATADIR; | ||||
|         let path = dir + '/gnome-shell-dbus-interfaces.gresource'; | ||||
|         let dir = GLib.getenv('GNOME_SHELL_DATADIR') || Config.PKGDATADIR; | ||||
|         let path = `${dir}/gnome-shell-dbus-interfaces.gresource`; | ||||
|         _ifaceResource = Gio.Resource.load(path); | ||||
|         _ifaceResource._register(); | ||||
|     } | ||||
|  | ||||
|     let xml = null; | ||||
|     let uri = 'resource:///org/gnome/shell/dbus-interfaces/' + iface + '.xml'; | ||||
|     let uri = `resource:///org/gnome/shell/dbus-interfaces/${iface}.xml`; | ||||
|     let f = Gio.File.new_for_uri(uri); | ||||
|  | ||||
|     try { | ||||
|   | ||||
| @@ -11,7 +11,7 @@ var PresenceStatus = { | ||||
|     AVAILABLE: 0, | ||||
|     INVISIBLE: 1, | ||||
|     BUSY: 2, | ||||
|     IDLE: 3 | ||||
|     IDLE: 3, | ||||
| }; | ||||
|  | ||||
| var PresenceProxy = Gio.DBusProxy.makeProxyWrapper(PresenceIface); | ||||
|   | ||||
| @@ -82,11 +82,11 @@ var HistoryManager = class { | ||||
|  | ||||
|     _onEntryKeyPress(entry, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol == Clutter.KEY_Up) { | ||||
|         if (symbol == Clutter.KEY_Up) | ||||
|             return this._setPrevItem(entry.get_text()); | ||||
|         } else if (symbol == Clutter.KEY_Down) { | ||||
|         else if (symbol == Clutter.KEY_Down) | ||||
|             return this._setNextItem(entry.get_text()); | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -142,7 +142,7 @@ var IBusManager = class { | ||||
|             }); | ||||
|             this._panelService.connect('focus-in', (panel, path) => { | ||||
|                 if (!GLib.str_has_suffix(path, '/InputContext_1')) | ||||
|                     this.emit ('focus-in'); | ||||
|                     this.emit('focus-in'); | ||||
|             }); | ||||
|             this._panelService.connect('focus-out', () => this.emit('focus-out')); | ||||
|  | ||||
| @@ -157,10 +157,10 @@ var IBusManager = class { | ||||
|             } catch (e) { | ||||
|             } | ||||
|             // If an engine is already active we need to get its properties | ||||
|             this._ibus.get_global_engine_async(-1, this._cancellable, (_bus, result) => { | ||||
|             this._ibus.get_global_engine_async(-1, this._cancellable, (_bus, res) => { | ||||
|                 let engine; | ||||
|                 try { | ||||
|                     engine = this._ibus.get_global_engine_async_finish(result); | ||||
|                     engine = this._ibus.get_global_engine_async_finish(res); | ||||
|                     if (!engine) | ||||
|                         return; | ||||
|                 } catch (e) { | ||||
|   | ||||
| @@ -48,7 +48,7 @@ class InputMethod extends Clutter.InputMethod { | ||||
|  | ||||
|     _onConnected() { | ||||
|         this._cancellable = new Gio.Cancellable(); | ||||
|         this._ibus.create_input_context_async ('gnome-shell', -1, | ||||
|         this._ibus.create_input_context_async('gnome-shell', -1, | ||||
|             this._cancellable, this._setContext.bind(this)); | ||||
|     } | ||||
|  | ||||
| @@ -69,6 +69,7 @@ class InputMethod extends Clutter.InputMethod { | ||||
|         this._context.connect('show-preedit-text', this._onShowPreeditText.bind(this)); | ||||
|         this._context.connect('hide-preedit-text', this._onHidePreeditText.bind(this)); | ||||
|         this._context.connect('forward-key-event', this._onForwardKeyEvent.bind(this)); | ||||
|         this._context.connect('destroy', this._clear.bind(this)); | ||||
|  | ||||
|         this._updateCapabilities(); | ||||
|     } | ||||
| @@ -128,7 +129,7 @@ class InputMethod extends Clutter.InputMethod { | ||||
|  | ||||
|     _onForwardKeyEvent(_context, keyval, keycode, state) { | ||||
|         let press = (state & IBus.ModifierType.RELEASE_MASK) == 0; | ||||
|         state &= ~(IBus.ModifierType.RELEASE_MASK); | ||||
|         state &= ~IBus.ModifierType.RELEASE_MASK; | ||||
|  | ||||
|         let curEvent = Clutter.get_current_event(); | ||||
|         let time; | ||||
| @@ -264,6 +265,9 @@ class InputMethod extends Clutter.InputMethod { | ||||
|             event.get_key_code() - 8, // Convert XKB keycodes to evcodes | ||||
|             state, -1, this._cancellable, | ||||
|             (context, res) => { | ||||
|                 if (context != this._context) | ||||
|                     return; | ||||
|  | ||||
|                 try { | ||||
|                     let retval = context.process_key_event_async_finish(res); | ||||
|                     this.notify_key_event(event, retval); | ||||
|   | ||||
| @@ -40,6 +40,15 @@ var IntrospectService = class { | ||||
|                         }); | ||||
|  | ||||
|         this._syncRunningApplications(); | ||||
|  | ||||
|         this._whitelistMap = new Map(); | ||||
|         APP_WHITELIST.forEach(appName => { | ||||
|             Gio.DBus.watch_name(Gio.BusType.SESSION, | ||||
|                 appName, | ||||
|                 Gio.BusNameWatcherFlags.NONE, | ||||
|                 (conn, name, owner) => this._whitelistMap.set(name, owner), | ||||
|                 (conn, name) => this._whitelistMap.delete(name)); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _isStandaloneApp(app) { | ||||
| @@ -51,7 +60,7 @@ var IntrospectService = class { | ||||
|     } | ||||
|  | ||||
|     _isSenderWhitelisted(sender) { | ||||
|         return APP_WHITELIST.includes(sender); | ||||
|         return [...this._whitelistMap.values()].includes(sender); | ||||
|     } | ||||
|  | ||||
|     _getSandboxedAppId(app) { | ||||
| @@ -70,7 +79,7 @@ var IntrospectService = class { | ||||
|  | ||||
|         for (let app of apps) { | ||||
|             let appInfo = {}; | ||||
|             let isAppActive = (focusedApp == app); | ||||
|             let isAppActive = focusedApp == app; | ||||
|  | ||||
|             if (!this._isStandaloneApp(app)) | ||||
|                 continue; | ||||
| @@ -104,10 +113,10 @@ var IntrospectService = class { | ||||
|             return false; | ||||
|  | ||||
|         let type = window.get_window_type(); | ||||
|         return (type == Meta.WindowType.NORMAL || | ||||
|         return type == Meta.WindowType.NORMAL || | ||||
|                 type == Meta.WindowType.DIALOG || | ||||
|                 type == Meta.WindowType.MODAL_DIALOG || | ||||
|                 type == Meta.WindowType.UTILITY); | ||||
|                 type == Meta.WindowType.UTILITY; | ||||
|     } | ||||
|  | ||||
|     GetRunningApplicationsAsync(params, invocation) { | ||||
| @@ -152,9 +161,9 @@ var IntrospectService = class { | ||||
|                     'app-id': GLib.Variant.new('s', app.get_id()), | ||||
|                     'client-type': GLib.Variant.new('u', window.get_client_type()), | ||||
|                     'is-hidden': GLib.Variant.new('b', window.is_hidden()), | ||||
|                     'has-focus': GLib.Variant.new('b', (window == focusWindow)), | ||||
|                     'has-focus': GLib.Variant.new('b', window == focusWindow), | ||||
|                     'width': GLib.Variant.new('u', frameRect.width), | ||||
|                     'height': GLib.Variant.new('u', frameRect.height) | ||||
|                     'height': GLib.Variant.new('u', frameRect.height), | ||||
|                 }; | ||||
|  | ||||
|                 // These properties may not be available for all windows: | ||||
| @@ -164,9 +173,10 @@ var IntrospectService = class { | ||||
|                 if (wmClass != null) | ||||
|                     windowsList[windowId]['wm-class'] = GLib.Variant.new('s', wmClass); | ||||
|  | ||||
|                 if (sandboxedAppId != null) | ||||
|                 if (sandboxedAppId != null) { | ||||
|                     windowsList[windowId]['sandboxed-app-id'] = | ||||
|                         GLib.Variant.new('s', sandboxedAppId); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         invocation.return_value(new GLib.Variant('(a{ta{sv}})', [windowsList])); | ||||
|   | ||||
| @@ -11,9 +11,8 @@ function getCompletions(text, commandHeader, globalCompletionList) { | ||||
|     let methods = []; | ||||
|     let expr_, base; | ||||
|     let attrHead = ''; | ||||
|     if (globalCompletionList == null) { | ||||
|     if (globalCompletionList == null) | ||||
|         globalCompletionList = []; | ||||
|     } | ||||
|  | ||||
|     let offset = getExpressionOffset(text, text.length - 1); | ||||
|     if (offset >= 0) { | ||||
| @@ -59,9 +58,8 @@ function isStopChar(c) { | ||||
| function findMatchingQuote(expr, offset) { | ||||
|     let quoteChar = expr.charAt(offset); | ||||
|     for (let i = offset - 1; i >= 0; --i) { | ||||
|         if (expr.charAt(i) == quoteChar && expr.charAt(i - 1) != '\\') { | ||||
|         if (expr.charAt(i) == quoteChar && expr.charAt(i - 1) != '\\') | ||||
|             return i; | ||||
|         } | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
| @@ -69,9 +67,8 @@ function findMatchingQuote(expr, offset) { | ||||
| // Given the ending position of a regex, find where it starts | ||||
| function findMatchingSlash(expr, offset) { | ||||
|     for (let i = offset - 1; i >= 0; --i) { | ||||
|         if (expr.charAt(i) == '/' && expr.charAt(i - 1) != '\\') { | ||||
|         if (expr.charAt(i) == '/' && expr.charAt(i - 1) != '\\') | ||||
|             return i; | ||||
|         } | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
| @@ -82,31 +79,30 @@ function findMatchingSlash(expr, offset) { | ||||
| // findMatchingBrace("[(])", 3) returns 1. | ||||
| function findMatchingBrace(expr, offset) { | ||||
|     let closeBrace = expr.charAt(offset); | ||||
|     let openBrace = ({ ')': '(', ']': '[' })[closeBrace]; | ||||
|     let openBrace = { ')': '(', ']': '[' }[closeBrace]; | ||||
|  | ||||
|     function findTheBrace(expr, offset) { | ||||
|         if (offset < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|     return findTheBrace(expr, offset - 1, openBrace, closeBrace); | ||||
| } | ||||
|  | ||||
|         if (expr.charAt(offset) == openBrace) { | ||||
|             return offset; | ||||
|         } | ||||
|         if (expr.charAt(offset).match(/['"]/)) { | ||||
|             return findTheBrace(expr, findMatchingQuote(expr, offset) - 1); | ||||
|         } | ||||
|         if (expr.charAt(offset) == '/') { | ||||
|             return findTheBrace(expr, findMatchingSlash(expr, offset) - 1); | ||||
|         } | ||||
|         if (expr.charAt(offset) == closeBrace) { | ||||
|             return findTheBrace(expr, findTheBrace(expr, offset - 1) - 1); | ||||
|         } | ||||
| function findTheBrace(expr, offset, ...braces) { | ||||
|     let [openBrace, closeBrace] = braces; | ||||
|  | ||||
|         return findTheBrace(expr, offset - 1); | ||||
|     if (offset < 0) | ||||
|         return -1; | ||||
|  | ||||
|     } | ||||
|     if (expr.charAt(offset) == openBrace) | ||||
|         return offset; | ||||
|  | ||||
|     return findTheBrace(expr, offset - 1); | ||||
|     if (expr.charAt(offset).match(/['"]/)) | ||||
|         return findTheBrace(expr, findMatchingQuote(expr, offset) - 1, ...braces); | ||||
|  | ||||
|     if (expr.charAt(offset) == '/') | ||||
|         return findTheBrace(expr, findMatchingSlash(expr, offset) - 1, ...braces); | ||||
|  | ||||
|     if (expr.charAt(offset) == closeBrace) | ||||
|         return findTheBrace(expr, findTheBrace(expr, offset - 1, ...braces) - 1, ...braces); | ||||
|  | ||||
|     return findTheBrace(expr, offset - 1, ...braces); | ||||
| } | ||||
|  | ||||
| // Walk expr backwards from offset looking for the beginning of an | ||||
| @@ -118,13 +114,11 @@ function getExpressionOffset(expr, offset) { | ||||
|     while (offset >= 0) { | ||||
|         let currChar = expr.charAt(offset); | ||||
|  | ||||
|         if (isStopChar(currChar)) { | ||||
|         if (isStopChar(currChar)) | ||||
|             return offset + 1; | ||||
|         } | ||||
|  | ||||
|         if (currChar.match(/[)\]]/)) { | ||||
|         if (currChar.match(/[)\]]/)) | ||||
|             offset = findMatchingBrace(expr, offset); | ||||
|         } | ||||
|  | ||||
|         --offset; | ||||
|     } | ||||
| @@ -141,10 +135,10 @@ function isValidPropertyName(w) { | ||||
| // To get all properties (enumerable and not), we need to walk | ||||
| // the prototype chain ourselves | ||||
| function getAllProps(obj) { | ||||
|     if (obj === null || obj === undefined) { | ||||
|     if (obj === null || obj === undefined) | ||||
|         return []; | ||||
|     } | ||||
|     return Object.getOwnPropertyNames(obj).concat( getAllProps(Object.getPrototypeOf(obj)) ); | ||||
|  | ||||
|     return Object.getOwnPropertyNames(obj).concat(getAllProps(Object.getPrototypeOf(obj))); | ||||
| } | ||||
|  | ||||
| // Given a string _expr_, returns all methods | ||||
| @@ -168,7 +162,7 @@ function getPropertyNamesFromExpression(expr, commandHeader = '') { | ||||
|     if (typeof obj === 'object') { | ||||
|         let allProps = getAllProps(obj); | ||||
|         // Get only things we are allowed to complete following a '.' | ||||
|         allProps = allProps.filter( isValidPropertyName ); | ||||
|         allProps = allProps.filter(isValidPropertyName); | ||||
|  | ||||
|         // Make sure propsUnique contains one key for every | ||||
|         // property so we end up with a unique list of properties | ||||
| @@ -189,24 +183,26 @@ function getCommonPrefix(words) { | ||||
|     return word; | ||||
| } | ||||
|  | ||||
| // Remove any blocks that are quoted or are in a regex | ||||
| function removeLiterals(str) { | ||||
|     if (str.length == 0) | ||||
|         return ''; | ||||
|  | ||||
|     let currChar = str.charAt(str.length - 1); | ||||
|     if (currChar == '"' || currChar == '\'') { | ||||
|         return removeLiterals( | ||||
|             str.slice(0, findMatchingQuote(str, str.length - 1))); | ||||
|     } else if (currChar == '/') { | ||||
|         return removeLiterals( | ||||
|             str.slice(0, findMatchingSlash(str, str.length - 1))); | ||||
|     } | ||||
|  | ||||
|     return removeLiterals(str.slice(0, str.length - 1)) + currChar; | ||||
| } | ||||
|  | ||||
| // Returns true if there is reason to think that eval(str) | ||||
| // will modify the global scope | ||||
| function isUnsafeExpression(str) { | ||||
|     // Remove any blocks that are quoted or are in a regex | ||||
|     function removeLiterals(str) { | ||||
|         if (str.length == 0) { | ||||
|             return ''; | ||||
|         } | ||||
|  | ||||
|         let currChar = str.charAt(str.length - 1); | ||||
|         if (currChar == '"' || currChar == '\'') { | ||||
|             return removeLiterals(str.slice(0, findMatchingQuote(str, str.length - 1))); | ||||
|         } else if (currChar == '/') { | ||||
|             return removeLiterals(str.slice(0, findMatchingSlash(str, str.length - 1))); | ||||
|         } | ||||
|  | ||||
|         return removeLiterals(str.slice(0, str.length - 1)) + currChar; | ||||
|     } | ||||
|  | ||||
|     // Check for any sort of assignment | ||||
|     // The strategy used is dumb: remove any quotes | ||||
| @@ -214,8 +210,8 @@ function isUnsafeExpression(str) { | ||||
|     // If there is, it might be an unsafe assignment. | ||||
|  | ||||
|     let prunedStr = removeLiterals(str); | ||||
|     prunedStr = prunedStr.replace(/[=!]==/g, '');    //replace === and !== with nothing | ||||
|     prunedStr = prunedStr.replace(/[=<>!]=/g, '');    //replace ==, <=, >=, != with nothing | ||||
|     prunedStr = prunedStr.replace(/[=!]==/g, '');    // replace === and !== with nothing | ||||
|     prunedStr = prunedStr.replace(/[=<>!]=/g, '');    // replace ==, <=, >=, != with nothing | ||||
|  | ||||
|     if (prunedStr.match(/[=]/)) { | ||||
|         return true; | ||||
|   | ||||
| @@ -74,8 +74,9 @@ function registerSessionWithGDM() { | ||||
| let _loginManager = null; | ||||
|  | ||||
| /** | ||||
|  * LoginManager: | ||||
|  * getLoginManager: | ||||
|  * An abstraction over systemd/logind and ConsoleKit. | ||||
|  * @returns {object} - the LoginManager singleton | ||||
|  * | ||||
|  */ | ||||
| function getLoginManager() { | ||||
| @@ -103,21 +104,21 @@ var LoginManagerSystemd = class { | ||||
|  | ||||
|     getCurrentSessionProxy(callback) { | ||||
|         if (this._currentSession) { | ||||
|             callback (this._currentSession); | ||||
|             callback(this._currentSession); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let sessionId = GLib.getenv('XDG_SESSION_ID'); | ||||
|         if (!sessionId) { | ||||
|             log('Unset XDG_SESSION_ID, getCurrentSessionProxy() called outside a user session. Asking logind directly.'); | ||||
|             let [session, objectPath_] = this._userProxy.Display; | ||||
|             let [session, objectPath] = this._userProxy.Display; | ||||
|             if (session) { | ||||
|                 log(`Will monitor session ${session}`); | ||||
|                 sessionId = session; | ||||
|             } else { | ||||
|                 log('Failed to find "Display" session; are we the greeter?'); | ||||
|  | ||||
|                 for (let [session, objectPath] of this._userProxy.Sessions) { | ||||
|                 for ([session, objectPath] of this._userProxy.Sessions) { | ||||
|                     let sessionProxy = new SystemdLoginSession(Gio.DBus.system, | ||||
|                                                                'org.freedesktop.login1', | ||||
|                                                                objectPath); | ||||
| @@ -185,7 +186,7 @@ var LoginManagerSystemd = class { | ||||
|                 try { | ||||
|                     let [outVariant_, fdList] = proxy.call_with_unix_fd_list_finish(result); | ||||
|                     fd = fdList.steal_fds()[0]; | ||||
|                     callback(new Gio.UnixInputStream({ fd: fd })); | ||||
|                     callback(new Gio.UnixInputStream({ fd })); | ||||
|                 } catch (e) { | ||||
|                     logError(e, "Error getting systemd inhibitor"); | ||||
|                     callback(null); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported ModemBase, ModemGsm, ModemCdma, BroadbandModem  */ | ||||
|  | ||||
| const { Gio, NMA } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
| const { Gio, GObject, NM, NMA } = imports.gi; | ||||
|  | ||||
| const { loadInterfaceXML } = imports.misc.fileUtils; | ||||
|  | ||||
| @@ -98,21 +98,46 @@ const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInter | ||||
| const ModemCdmaInterface = loadInterfaceXML('org.freedesktop.ModemManager.Modem.Cdma'); | ||||
| const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface); | ||||
|  | ||||
| var ModemGsm = class { | ||||
|     constructor(path) { | ||||
|         this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path); | ||||
| var ModemBase = GObject.registerClass({ | ||||
|     GTypeFlags: GObject.TypeFlags.ABSTRACT, | ||||
|     Properties: { | ||||
|         'operator-name': GObject.ParamSpec.string( | ||||
|             'operator-name', 'operator-name', 'operator-name', | ||||
|             GObject.ParamFlags.READABLE, | ||||
|             null), | ||||
|         'signal-quality': GObject.ParamSpec.int( | ||||
|             'signal-quality', 'signal-quality', 'signal-quality', | ||||
|             GObject.ParamFlags.READABLE, | ||||
|             0, 100, 0), | ||||
|     }, | ||||
| }, class ModemBase extends GObject.Object { | ||||
|     _setOperatorName(operatorName) { | ||||
|         if (this.operator_name == operatorName) | ||||
|             return; | ||||
|         this.operator_name = operatorName; | ||||
|         this.notify('operator-name'); | ||||
|     } | ||||
|  | ||||
|         this.signal_quality = 0; | ||||
|         this.operator_name = null; | ||||
|     _setSignalQuality(signalQuality) { | ||||
|         if (this.signal_quality == signalQuality) | ||||
|             return; | ||||
|         this.signal_quality = signalQuality; | ||||
|         this.notify('signal-quality'); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var ModemGsm = GObject.registerClass( | ||||
| class ModemGsm extends ModemBase { | ||||
|     _init(path) { | ||||
|         super._init(); | ||||
|         this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path); | ||||
|  | ||||
|         // Code is duplicated because the function have different signatures | ||||
|         this._proxy.connectSignal('SignalQuality', (proxy, sender, [quality]) => { | ||||
|             this.signal_quality = quality; | ||||
|             this.emit('notify::signal-quality'); | ||||
|             this._setSignalQuality(quality); | ||||
|         }); | ||||
|         this._proxy.connectSignal('RegistrationInfo', (proxy, sender, [_status, code, name]) => { | ||||
|             this.operator_name = _findProviderForMccMnc(name, code); | ||||
|             this.emit('notify::operator-name'); | ||||
|             this._setOperatorName(_findProviderForMccMnc(name, code)); | ||||
|         }); | ||||
|         this._proxy.GetRegistrationInfoRemote(([result], err) => { | ||||
|             if (err) { | ||||
| @@ -121,32 +146,28 @@ var ModemGsm = class { | ||||
|             } | ||||
|  | ||||
|             let [status_, code, name] = result; | ||||
|             this.operator_name = _findProviderForMccMnc(name, code); | ||||
|             this.emit('notify::operator-name'); | ||||
|             this._setOperatorName(_findProviderForMccMnc(name, code)); | ||||
|         }); | ||||
|         this._proxy.GetSignalQualityRemote((result, err) => { | ||||
|             if (err) { | ||||
|                 // it will return an error if the device is not connected | ||||
|                 this.signal_quality = 0; | ||||
|                 this._setSignalQuality(0); | ||||
|             } else { | ||||
|                 let [quality] = result; | ||||
|                 this.signal_quality = quality; | ||||
|                 this._setSignalQuality(quality); | ||||
|             } | ||||
|             this.emit('notify::signal-quality'); | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(ModemGsm.prototype); | ||||
| }); | ||||
|  | ||||
| var ModemCdma = class { | ||||
|     constructor(path) { | ||||
| var ModemCdma = GObject.registerClass( | ||||
| class ModemCdma extends ModemBase { | ||||
|     _init(path) { | ||||
|         super._init(); | ||||
|         this._proxy = new ModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path); | ||||
|  | ||||
|         this.signal_quality = 0; | ||||
|         this.operator_name = null; | ||||
|         this._proxy.connectSignal('SignalQuality', (proxy, sender, params) => { | ||||
|             this.signal_quality = params[0]; | ||||
|             this.emit('notify::signal-quality'); | ||||
|             this._setSignalQuality(params[0]); | ||||
|  | ||||
|             // receiving this signal means the device got activated | ||||
|             // and we can finally call GetServingSystem | ||||
| @@ -156,12 +177,11 @@ var ModemCdma = class { | ||||
|         this._proxy.GetSignalQualityRemote((result, err) => { | ||||
|             if (err) { | ||||
|                 // it will return an error if the device is not connected | ||||
|                 this.signal_quality = 0; | ||||
|                 this._setSignalQuality(0); | ||||
|             } else { | ||||
|                 let [quality] = result; | ||||
|                 this.signal_quality = quality; | ||||
|                 this._setSignalQuality(quality); | ||||
|             } | ||||
|             this.emit('notify::signal-quality'); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -169,17 +189,14 @@ var ModemCdma = class { | ||||
|         this._proxy.GetServingSystemRemote(([result], err) => { | ||||
|             if (err) { | ||||
|                 // it will return an error if the device is not connected | ||||
|                 this.operator_name = null; | ||||
|                 this._setOperatorName(null); | ||||
|             } else { | ||||
|                 let [bandClass_, band_, sid] = result; | ||||
|  | ||||
|                 this.operator_name = _findProviderForSid(sid); | ||||
|                 this._setOperatorName(_findProviderForSid(sid)); | ||||
|             } | ||||
|             this.emit('notify::operator-name'); | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(ModemCdma.prototype); | ||||
| }); | ||||
|  | ||||
|  | ||||
| // ------------------------------------------------------- // | ||||
| @@ -195,12 +212,20 @@ const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gp | ||||
| const BroadbandModemCdmaInterface = loadInterfaceXML('org.freedesktop.ModemManager1.Modem.ModemCdma'); | ||||
| const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface); | ||||
|  | ||||
| var BroadbandModem = class { | ||||
|     constructor(path, capabilities) { | ||||
|         this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path); | ||||
| var BroadbandModem = GObject.registerClass({ | ||||
|     Properties: { | ||||
|         'capabilities': GObject.ParamSpec.flags( | ||||
|             'capabilities', 'capabilities', 'capabilities', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             NM.DeviceModemCapabilities.$gtype, | ||||
|             NM.DeviceModemCapabilities.NONE), | ||||
|     }, | ||||
| }, class BroadbandModem extends ModemBase { | ||||
|     _init(path, capabilities) { | ||||
|         super._init({ capabilities }); | ||||
|         this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path); | ||||
|         this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path); | ||||
|         this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path); | ||||
|         this._capabilities = capabilities; | ||||
|  | ||||
|         this._proxy.connect('g-properties-changed', (proxy, properties) => { | ||||
|             if ('SignalQuality' in properties.deep_unpack()) | ||||
| @@ -224,9 +249,8 @@ var BroadbandModem = class { | ||||
|     } | ||||
|  | ||||
|     _reloadSignalQuality() { | ||||
|         let [quality, recent_] = this._proxy.SignalQuality; | ||||
|         this.signal_quality = quality; | ||||
|         this.emit('notify::signal-quality'); | ||||
|         let [quality, recent_] = this.SignalQuality; | ||||
|         this._setSignalQuality(quality); | ||||
|     } | ||||
|  | ||||
|     _reloadOperatorName() { | ||||
| @@ -240,8 +264,7 @@ var BroadbandModem = class { | ||||
|             newName += this.operator_name_cdma; | ||||
|         } | ||||
|  | ||||
|         this.operator_name = newName; | ||||
|         this.emit('notify::operator-name'); | ||||
|         this._setOperatorName(newName); | ||||
|     } | ||||
|  | ||||
|     _reload3gppOperatorName() { | ||||
| @@ -256,5 +279,4 @@ var BroadbandModem = class { | ||||
|         this.operator_name_cdma = _findProviderForSid(sid); | ||||
|         this._reloadOperatorName(); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(BroadbandModem.prototype); | ||||
| }); | ||||
|   | ||||
| @@ -192,9 +192,8 @@ var ObjectManager = class { | ||||
|     _onNameAppeared() { | ||||
|         this._managerProxy.GetManagedObjectsRemote((result, error) => { | ||||
|             if (!result) { | ||||
|                 if (error) { | ||||
|                 if (error) | ||||
|                     logError(error, `could not get remote objects for service ${this._serviceName} path ${this._managerPath}`); | ||||
|                 } | ||||
|  | ||||
|                 this._tryToCompleteLoad(); | ||||
|                 return; | ||||
|   | ||||
| @@ -17,9 +17,10 @@ | ||||
| // @params and @defaults | ||||
| function parse(params = {}, defaults, allowExtras) { | ||||
|     if (!allowExtras) { | ||||
|         for (let prop in params) | ||||
|         for (let prop in params) { | ||||
|             if (!(prop in defaults)) | ||||
|                 throw new Error(`Unrecognized parameter "${prop}"`); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let defaultsCopy = Object.assign({}, defaults); | ||||
|   | ||||
| @@ -72,11 +72,10 @@ var SmartcardManager = class { | ||||
|             if ('IsInserted' in properties.deep_unpack()) { | ||||
|                 this._updateToken(token); | ||||
|  | ||||
|                 if (token.IsInserted) { | ||||
|                 if (token.IsInserted) | ||||
|                     this.emit('smartcard-inserted', token); | ||||
|                 } else { | ||||
|                 else | ||||
|                     this.emit('smartcard-removed', token); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|   | ||||
| @@ -74,8 +74,8 @@ const SystemActions = GObject.registerClass({ | ||||
|                                                           'orientation-lock-icon', | ||||
|                                                           'orientation-lock-icon', | ||||
|                                                           GObject.ParamFlags.READWRITE, | ||||
|                                                           null) | ||||
|     } | ||||
|                                                           null), | ||||
|     }, | ||||
| }, class SystemActions extends GObject.Object { | ||||
|     _init() { | ||||
|         super._init(); | ||||
| @@ -90,7 +90,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             iconName: 'system-shutdown-symbolic', | ||||
|             // Translators: A list of keywords that match the power-off action, separated by semicolons | ||||
|             keywords: _("power off;shutdown;reboot;restart").split(/[; ]/), | ||||
|             available: false | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(LOCK_SCREEN_ACTION_ID, { | ||||
|             // Translators: The name of the lock screen action in search | ||||
| @@ -98,7 +98,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             iconName: 'system-lock-screen-symbolic', | ||||
|             // Translators: A list of keywords that match the lock screen action, separated by semicolons | ||||
|             keywords: _("lock screen").split(/[; ]/), | ||||
|             available: false | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(LOGOUT_ACTION_ID, { | ||||
|             // Translators: The name of the logout action in search | ||||
| @@ -106,7 +106,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             iconName: 'application-exit-symbolic', | ||||
|             // Translators: A list of keywords that match the logout action, separated by semicolons | ||||
|             keywords: _("logout;log out;sign off").split(/[; ]/), | ||||
|             available: false | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(SUSPEND_ACTION_ID, { | ||||
|             // Translators: The name of the suspend action in search | ||||
| @@ -114,7 +114,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             iconName: 'media-playback-pause-symbolic', | ||||
|             // Translators: A list of keywords that match the suspend action, separated by semicolons | ||||
|             keywords: _("suspend;sleep").split(/[; ]/), | ||||
|             available: false | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(SWITCH_USER_ACTION_ID, { | ||||
|             // Translators: The name of the switch user action in search | ||||
| @@ -122,7 +122,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             iconName: 'system-switch-user-symbolic', | ||||
|             // Translators: A list of keywords that match the switch user action, separated by semicolons | ||||
|             keywords: _("switch user").split(/[; ]/), | ||||
|             available: false | ||||
|             available: false, | ||||
|         }); | ||||
|         this._actions.set(LOCK_ORIENTATION_ACTION_ID, { | ||||
|             // Translators: The name of the lock orientation action in search | ||||
| @@ -130,7 +130,7 @@ const SystemActions = GObject.registerClass({ | ||||
|             iconName: '', | ||||
|             // Translators: A list of keywords that match the lock orientation action, separated by semicolons | ||||
|             keywords: _("lock orientation;screen;rotation").split(/[; ]/), | ||||
|             available: false | ||||
|             available: false, | ||||
|         }); | ||||
|  | ||||
|         this._loginScreenSettings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA }); | ||||
| @@ -233,9 +233,10 @@ const SystemActions = GObject.registerClass({ | ||||
|  | ||||
|     _updateOrientationLock() { | ||||
|         let available = false; | ||||
|         if (this._sensorProxy.g_name_owner) | ||||
|         if (this._sensorProxy.g_name_owner) { | ||||
|             available = this._sensorProxy.HasAccelerometer && | ||||
|                         this._monitorManager.get_is_builtin_display_on(); | ||||
|         } | ||||
|  | ||||
|         this._actions.get(LOCK_ORIENTATION_ACTION_ID).available = available; | ||||
|  | ||||
| @@ -273,9 +274,10 @@ const SystemActions = GObject.registerClass({ | ||||
|  | ||||
|         let results = []; | ||||
|  | ||||
|         for (let [key, { available, keywords }] of this._actions) | ||||
|         for (let [key, { available, keywords }] of this._actions) { | ||||
|             if (available && terms.every(t => keywords.some(k => k.startsWith(t)))) | ||||
|                 results.push(key); | ||||
|         } | ||||
|  | ||||
|         return results; | ||||
|     } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnCommandLine, | ||||
|             formatTime, formatTimeSpan, createTimeLabel, insertSorted, | ||||
|             makeCloseButton, ensureActorVisibleInScrollView */ | ||||
|             makeCloseButton, ensureActorVisibleInScrollView, wiggle */ | ||||
|  | ||||
| const { Clutter, Gio, GLib, GObject, Shell, St } = imports.gi; | ||||
| const { Clutter, Gio, GLib, GObject, Shell, St, GnomeDesktop } = imports.gi; | ||||
| const Gettext = imports.gettext; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| @@ -121,12 +121,15 @@ function trySpawn(argv) { | ||||
|             // We are only interested in the part in the parentheses. (And | ||||
|             // we can't pattern match the text, since it gets localized.) | ||||
|             let message = err.message.replace(/.*\((.+)\)/, '$1'); | ||||
|             throw new (err.constructor)({ code: err.code, | ||||
|                                           message: message }); | ||||
|             throw new err.constructor({ code: err.code, message }); | ||||
|         } else { | ||||
|             throw err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Async call, we don't need the reply though | ||||
|     GnomeDesktop.start_systemd_scope(argv[0], pid, null, null, null, () => {}); | ||||
|  | ||||
|     // Dummy child watch; we don't want to double-fork internally | ||||
|     // because then we lose the parent-child relationship, which | ||||
|     // can break polkit.  See https://bugzilla.redhat.com//show_bug.cgi?id=819275 | ||||
| @@ -172,23 +175,28 @@ function formatTimeSpan(date) { | ||||
|  | ||||
|     if (minutesAgo < 5) | ||||
|         return _("Just now"); | ||||
|     if (hoursAgo < 1) | ||||
|     if (hoursAgo < 1) { | ||||
|         return Gettext.ngettext("%d minute ago", | ||||
|                                 "%d minutes ago", minutesAgo).format(minutesAgo); | ||||
|     if (daysAgo < 1) | ||||
|     } | ||||
|     if (daysAgo < 1) { | ||||
|         return Gettext.ngettext("%d hour ago", | ||||
|                                 "%d hours ago", hoursAgo).format(hoursAgo); | ||||
|     } | ||||
|     if (daysAgo < 2) | ||||
|         return _("Yesterday"); | ||||
|     if (daysAgo < 15) | ||||
|     if (daysAgo < 15) { | ||||
|         return Gettext.ngettext("%d day ago", | ||||
|                                 "%d days ago", daysAgo).format(daysAgo); | ||||
|     if (weeksAgo < 8) | ||||
|     } | ||||
|     if (weeksAgo < 8) { | ||||
|         return Gettext.ngettext("%d week ago", | ||||
|                                 "%d weeks ago", weeksAgo).format(weeksAgo); | ||||
|     if (yearsAgo < 1) | ||||
|     } | ||||
|     if (yearsAgo < 1) { | ||||
|         return Gettext.ngettext("%d month ago", | ||||
|                                 "%d months ago", monthsAgo).format(monthsAgo); | ||||
|     } | ||||
|     return Gettext.ngettext("%d year ago", | ||||
|                             "%d years ago", yearsAgo).format(yearsAgo); | ||||
| } | ||||
| @@ -213,7 +221,10 @@ function formatTime(time, params) { | ||||
|         _desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' }); | ||||
|     let clockFormat = _desktopSettings.get_string('clock-format'); | ||||
|  | ||||
|     params = Params.parse(params, { timeOnly: false }); | ||||
|     params = Params.parse(params, { | ||||
|         timeOnly: false, | ||||
|         ampm: true, | ||||
|     }); | ||||
|  | ||||
|     if (clockFormat == '24h') { | ||||
|         // Show only the time if date is on today | ||||
| @@ -246,7 +257,7 @@ function formatTime(time, params) { | ||||
|             format = N_("%B %-d %Y, %H\u2236%M"); | ||||
|     } else { | ||||
|         // Show only the time if date is on today | ||||
|         if (daysAgo < 1 || params.timeOnly) | ||||
|         if (daysAgo < 1 || params.timeOnly) // eslint-disable-line no-lonely-if | ||||
|             /* Translators: Time in 12h format */ | ||||
|             format = N_("%l\u2236%M %p"); | ||||
|         // Show the word "Yesterday" and time if date is on yesterday | ||||
| @@ -275,6 +286,11 @@ function formatTime(time, params) { | ||||
|             format = N_("%B %-d %Y, %l\u2236%M %p"); | ||||
|     } | ||||
|  | ||||
|     // Time in short 12h format, without the equivalent of "AM" or "PM"; used | ||||
|     // when it is clear from the context | ||||
|     if (!params.ampm) | ||||
|         format = format.replace(/\s*%p/g, ''); | ||||
|  | ||||
|     let formattedTime = date.format(Shell.util_translate_time_string(format)); | ||||
|     // prepend LTR-mark to colon/ratio to force a text direction on times | ||||
|     return formattedTime.replace(/([:\u2236])/g, '\u200e$1'); | ||||
| @@ -325,7 +341,7 @@ function lowerBound(array, val, cmp) { | ||||
|             max = mid; | ||||
|     } | ||||
|  | ||||
|     return (min == max || cmp(array[min], val) < 0) ? max : min; | ||||
|     return min == max || cmp(array[min], val) < 0 ? max : min; | ||||
| } | ||||
|  | ||||
| // insertSorted: | ||||
| @@ -346,19 +362,13 @@ function insertSorted(array, val, cmp) { | ||||
| var CloseButton = GObject.registerClass( | ||||
| class CloseButton extends St.Button { | ||||
|     _init(boxpointer) { | ||||
|         super._init({ style_class: 'notification-close' }); | ||||
|  | ||||
|         // This is a bit tricky. St.Bin has its own x-align/y-align properties | ||||
|         // that compete with Clutter's properties. This should be fixed for | ||||
|         // Clutter 2.0. Since St.Bin doesn't define its own setters, the | ||||
|         // setters are a workaround to get Clutter's version. | ||||
|         this.set_x_align(Clutter.ActorAlign.END); | ||||
|         this.set_y_align(Clutter.ActorAlign.START); | ||||
|  | ||||
|         // XXX Clutter 2.0 workaround: ClutterBinLayout needs expand | ||||
|         // to respect the alignments. | ||||
|         this.set_x_expand(true); | ||||
|         this.set_y_expand(true); | ||||
|         super._init({ | ||||
|             style_class: 'notification-close', | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             x_align: Clutter.ActorAlign.END, | ||||
|             y_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|  | ||||
|         this._boxPointer = boxpointer; | ||||
|         if (boxpointer) | ||||
| @@ -411,7 +421,7 @@ function ensureActorVisibleInScrollView(scrollView, actor) { | ||||
|         if (!parent) | ||||
|             throw new Error("actor not in scroll view"); | ||||
|  | ||||
|         let box = parent.get_allocation_box(); | ||||
|         box = parent.get_allocation_box(); | ||||
|         y1 += box.y1; | ||||
|         y2 += box.y1; | ||||
|         parent = parent.get_parent(); | ||||
| @@ -426,6 +436,40 @@ function ensureActorVisibleInScrollView(scrollView, actor) { | ||||
|  | ||||
|     adjustment.ease(value, { | ||||
|         mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         duration: SCROLL_TIME | ||||
|         duration: SCROLL_TIME, | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function wiggle(actor, params) { | ||||
|     params = Params.parse(params, { | ||||
|         offset: 0, | ||||
|         duration: 0, | ||||
|         wiggleCount: 0, | ||||
|     }); | ||||
|     actor.translation_x = 0; | ||||
|  | ||||
|     // Accelerate before wiggling | ||||
|     actor.ease({ | ||||
|         translation_x: -params.offset, | ||||
|         duration: params.duration, | ||||
|         mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         onComplete: () => { | ||||
|             // Wiggle | ||||
|             actor.ease({ | ||||
|                 translation_x: params.offset, | ||||
|                 duration: params.duration, | ||||
|                 mode: Clutter.AnimationMode.LINEAR, | ||||
|                 repeatCount: params.wiggleCount, | ||||
|                 autoReverse: true, | ||||
|                 onComplete: () => { | ||||
|                     // Decelerate and return to the original position | ||||
|                     actor.ease({ | ||||
|                         translation_x: 0, | ||||
|                         duration: params.duration, | ||||
|                         mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|                     }); | ||||
|                 }, | ||||
|             }); | ||||
|         }, | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -32,6 +32,7 @@ var WeatherClient = class { | ||||
|         this._gclueStarting = false; | ||||
|         this._gclueLocationChangedId = 0; | ||||
|  | ||||
|         this._needsAuth = true; | ||||
|         this._weatherAuthorized = false; | ||||
|         this._permStore = new PermissionStore.PermissionStore((proxy, error) => { | ||||
|             if (error) { | ||||
| @@ -47,11 +48,11 @@ var WeatherClient = class { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this._permStore.LookupRemote('gnome', 'geolocation', (res, error) => { | ||||
|                 if (error) | ||||
|                     log(`Error looking up permission: ${error.message}`); | ||||
|             this._permStore.LookupRemote('gnome', 'geolocation', (res, err) => { | ||||
|                 if (err) | ||||
|                     log(`Error looking up permission: ${err.message}`); | ||||
|  | ||||
|                 let [perms, data] = error ? [{}, null] : res; | ||||
|                 let [perms, data] = err ? [{}, null] : res; | ||||
|                 let  params = ['gnome', 'geolocation', false, data, perms]; | ||||
|                 this._onPermStoreChanged(this._permStore, '', params); | ||||
|             }); | ||||
| @@ -90,7 +91,7 @@ var WeatherClient = class { | ||||
|             this._onWeatherProxyReady.bind(this)); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ | ||||
|             schema_id: 'org.gnome.shell.weather' | ||||
|             schema_id: 'org.gnome.shell.weather', | ||||
|         }); | ||||
|         this._settings.connect('changed::automatic-location', | ||||
|             this._onAutomaticLocationChanged.bind(this)); | ||||
| @@ -142,7 +143,7 @@ var WeatherClient = class { | ||||
|     get _useAutoLocation() { | ||||
|         return this._autoLocationRequested && | ||||
|                this._locationSettings.get_boolean('enabled') && | ||||
|                this._weatherAuthorized; | ||||
|                (!this._needsAuth || this._weatherAuthorized); | ||||
|     } | ||||
|  | ||||
|     _onWeatherProxyReady(o, res) { | ||||
| @@ -169,12 +170,19 @@ var WeatherClient = class { | ||||
|     } | ||||
|  | ||||
|     _onInstalledChanged() { | ||||
|         let hadApp = (this._weatherApp != null); | ||||
|         let hadApp = this._weatherApp != null; | ||||
|         this._weatherApp = this._appSystem.lookup_app(WEATHER_APP_ID); | ||||
|         let haveApp = (this._weatherApp != null); | ||||
|         let haveApp = this._weatherApp != null; | ||||
|  | ||||
|         if (hadApp !== haveApp) | ||||
|             this.emit('changed'); | ||||
|  | ||||
|         let neededAuth = this._needsAuth; | ||||
|         this._needsAuth = this._weatherApp === null || | ||||
|                           this._weatherApp.app_info.has_key('X-Flatpak'); | ||||
|  | ||||
|         if (neededAuth !== this._needsAuth) | ||||
|             this._updateAutoLocation(); | ||||
|     } | ||||
|  | ||||
|     _loadInfo() { | ||||
| @@ -205,7 +213,7 @@ var WeatherClient = class { | ||||
|  | ||||
|         this._weatherInfo.abort(); | ||||
|         this._weatherInfo.set_location(location); | ||||
|         this._locationValid = (location != null); | ||||
|         this._locationValid = location != null; | ||||
|  | ||||
|         this._weatherInfo.set_enabled_providers(location ? this._providers : 0); | ||||
|  | ||||
|   | ||||
| @@ -57,7 +57,7 @@ var METRICS = { | ||||
|       units: "us" }, | ||||
|     applicationsShowTimeSubsequent: | ||||
|     { description: "Time to switch to applications view, second time", | ||||
|       units: "us" } | ||||
|       units: "us" }, | ||||
| }; | ||||
|  | ||||
| let WINDOW_CONFIGS = [ | ||||
| @@ -67,7 +67,7 @@ let WINDOW_CONFIGS = [ | ||||
|     { width: 640, height: 480, alpha: false, maximized: true,  count: 5,  metric: 'overviewFps5Maximized' }, | ||||
|     { width: 640, height: 480, alpha: false, maximized: true,  count: 10, metric: 'overviewFps10Maximized' }, | ||||
|     { width: 640, height: 480, alpha: true,  maximized: false, count: 5,  metric: 'overviewFps5Alpha' }, | ||||
|     { width: 640, height: 480, alpha: true,  maximized: false, count: 10, metric: 'overviewFps10Alpha' } | ||||
|     { width: 640, height: 480, alpha: true,  maximized: false, count: 10, metric: 'overviewFps10Alpha' }, | ||||
| ]; | ||||
|  | ||||
| function *run() { | ||||
| @@ -94,11 +94,12 @@ function *run() { | ||||
|             let config = WINDOW_CONFIGS[i / 2]; | ||||
|             yield Scripting.destroyTestWindows(); | ||||
|  | ||||
|             for (let k = 0; k < config.count; k++) | ||||
|             for (let k = 0; k < config.count; k++) { | ||||
|                 yield Scripting.createTestWindow({ width: config.width, | ||||
|                                                    height: config.height, | ||||
|                                                    alpha: config.alpha, | ||||
|                                                    maximized: config.maximized }); | ||||
|             } | ||||
|  | ||||
|             yield Scripting.waitTestWindows(); | ||||
|             yield Scripting.sleep(1000); | ||||
| @@ -127,11 +128,11 @@ function *run() { | ||||
|     for (let i = 0; i < 2; i++) { | ||||
|         Scripting.scriptEvent('applicationsShowStart'); | ||||
|         // eslint-disable-next-line require-atomic-updates | ||||
|         Main.overview._dash.showAppsButton.checked = true; | ||||
|         Main.overview.dash.showAppsButton.checked = true; | ||||
|         yield Scripting.waitLeisure(); | ||||
|         Scripting.scriptEvent('applicationsShowDone'); | ||||
|         // eslint-disable-next-line require-atomic-updates | ||||
|         Main.overview._dash.showAppsButton.checked = false; | ||||
|         Main.overview.dash.showAppsButton.checked = false; | ||||
|         yield Scripting.waitLeisure(); | ||||
|     } | ||||
| } | ||||
| @@ -174,11 +175,10 @@ function script_applicationsShowDone(time) { | ||||
| } | ||||
|  | ||||
| function script_afterShowHide(_time) { | ||||
|     if (overviewShowCount == 1) { | ||||
|     if (overviewShowCount == 1) | ||||
|         METRICS.usedAfterOverview.value = mallocUsedSize; | ||||
|     } else { | ||||
|     else | ||||
|         METRICS.leakedAfterOverview.value = mallocUsedSize - METRICS.usedAfterOverview.value; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function malloc_usedSize(time, bytes) { | ||||
|   | ||||
| @@ -114,7 +114,7 @@ function *run() { | ||||
|     Scripting.scriptEvent('desktopShown'); | ||||
|  | ||||
|     let interfaceSettings = new Gio.Settings({ | ||||
|         schema_id: 'org.gnome.desktop.interface' | ||||
|         schema_id: 'org.gnome.desktop.interface', | ||||
|     }); | ||||
|     interfaceSettings.set_boolean('enable-animations', false); | ||||
|  | ||||
| @@ -127,7 +127,7 @@ function *run() { | ||||
|  | ||||
|     Scripting.scriptEvent('applicationsShowStart'); | ||||
|     // eslint-disable-next-line require-atomic-updates | ||||
|     Main.overview._dash.showAppsButton.checked = true; | ||||
|     Main.overview.dash.showAppsButton.checked = true; | ||||
|  | ||||
|     yield Scripting.waitLeisure(); | ||||
|     Scripting.scriptEvent('applicationsShowDone'); | ||||
|   | ||||
| @@ -11,17 +11,17 @@ const { loadInterfaceXML } = imports.misc.fileUtils; | ||||
| const PortalHelperResult = { | ||||
|     CANCELLED: 0, | ||||
|     COMPLETED: 1, | ||||
|     RECHECK: 2 | ||||
|     RECHECK: 2, | ||||
| }; | ||||
|  | ||||
| const PortalHelperSecurityLevel = { | ||||
|     NOT_YET_DETERMINED: 0, | ||||
|     SECURE: 1, | ||||
|     INSECURE: 2 | ||||
|     INSECURE: 2, | ||||
| }; | ||||
|  | ||||
| const CONNECTIVITY_CHECK_HOST = 'nmcheck.gnome.org'; | ||||
| const CONNECTIVITY_CHECK_URI = 'http://' + CONNECTIVITY_CHECK_HOST; | ||||
| const CONNECTIVITY_CHECK_URI = `http://${CONNECTIVITY_CHECK_HOST}`; | ||||
| const CONNECTIVITY_RECHECK_RATELIMIT_TIMEOUT = 30 * GLib.USEC_PER_SEC; | ||||
|  | ||||
| const HelperDBusInterface = loadInterfaceXML('org.gnome.Shell.PortalHelper'); | ||||
| @@ -92,7 +92,7 @@ class PortalHeaderBar extends Gtk.HeaderBar { | ||||
| var PortalWindow = GObject.registerClass( | ||||
| class PortalWindow extends Gtk.ApplicationWindow { | ||||
|     _init(application, url, timestamp, doneCallback) { | ||||
|         super._init({ application: application }); | ||||
|         super._init({ application }); | ||||
|  | ||||
|         this.connect('delete-event', this.destroyWindow.bind(this)); | ||||
|         this._headerBar = new PortalHeaderBar(); | ||||
| @@ -287,7 +287,7 @@ class WebPortalHelper extends Gtk.Application { | ||||
|     } | ||||
|  | ||||
|     Authenticate(connection, url, timestamp) { | ||||
|         this._queue.push({ connection: connection, url: url, timestamp: timestamp }); | ||||
|         this._queue.push({ connection, url, timestamp }); | ||||
|  | ||||
|         this._processQueue(); | ||||
|     } | ||||
|   | ||||
| @@ -13,7 +13,7 @@ const AccessIface = loadInterfaceXML('org.freedesktop.impl.portal.Access'); | ||||
| var DialogResponse = { | ||||
|     OK: 0, | ||||
|     CANCEL: 1, | ||||
|     CLOSED: 2 | ||||
|     CLOSED: 2, | ||||
| }; | ||||
|  | ||||
| var AccessDialog = GObject.registerClass( | ||||
| @@ -35,7 +35,7 @@ class AccessDialog extends ModalDialog.ModalDialog { | ||||
|  | ||||
|     _buildLayout(title, subtitle, body, options) { | ||||
|         // No support for non-modal system dialogs, so ignore the option | ||||
|         //let modal = options['modal'] || true; | ||||
|         // let modal = options['modal'] || true; | ||||
|         let denyLabel = options['deny_label'] || _("Deny Access"); | ||||
|         let grantLabel = options['grant_label'] || _("Grant Access"); | ||||
|         let iconName = options['icon'] || null; | ||||
| @@ -56,8 +56,8 @@ class AccessDialog extends ModalDialog.ModalDialog { | ||||
|  | ||||
|             let check = new CheckBox.CheckBox(); | ||||
|             check.getLabelActor().text = name; | ||||
|             check.actor.checked = selected == "true"; | ||||
|             content.insertBeforeBody(check.actor); | ||||
|             check.checked = selected == "true"; | ||||
|             content.insertBeforeBody(check); | ||||
|  | ||||
|             this._choices.set(id, check); | ||||
|         } | ||||
| @@ -99,7 +99,7 @@ class AccessDialog extends ModalDialog.ModalDialog { | ||||
|         let results = {}; | ||||
|         if (response == DialogResponse.OK) { | ||||
|             for (let [id, check] of this._choices) { | ||||
|                 let checked = check.actor.checked ? 'true' : 'false'; | ||||
|                 let checked = check.checked ? 'true' : 'false'; | ||||
|                 results[id] = new GLib.Variant('s', checked); | ||||
|             } | ||||
|         } | ||||
|   | ||||
							
								
								
									
										152
									
								
								js/ui/altTab.js
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								js/ui/altTab.js
									
									
									
									
									
								
							| @@ -62,7 +62,7 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup { | ||||
|  | ||||
|         this.thumbnailsVisible = false; | ||||
|  | ||||
|         let apps = Shell.AppSystem.get_default().get_running (); | ||||
|         let apps = Shell.AppSystem.get_default().get_running(); | ||||
|  | ||||
|         if (apps.length == 0) | ||||
|             return; | ||||
| @@ -111,14 +111,12 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup { | ||||
|  | ||||
|     _initialSelection(backward, binding) { | ||||
|         if (binding == 'switch-group') { | ||||
|             if (backward) { | ||||
|             if (backward) | ||||
|                 this._select(0, this._items[0].cachedWindows.length - 1); | ||||
|             } else { | ||||
|                 if (this._items[0].cachedWindows.length > 1) | ||||
|                     this._select(0, 1); | ||||
|                 else | ||||
|                     this._select(0, 0); | ||||
|             } | ||||
|             else if (this._items[0].cachedWindows.length > 1) | ||||
|                 this._select(0, 1); | ||||
|             else | ||||
|                 this._select(0, 0); | ||||
|         } else if (binding == 'switch-group-backward') { | ||||
|             this._select(0, this._items[0].cachedWindows.length - 1); | ||||
|         } else if (binding == 'switch-applications-backward') { | ||||
| @@ -180,28 +178,27 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup { | ||||
|             this._select(this._next()); | ||||
|         } else if (action == Meta.KeyBindingAction.SWITCH_APPLICATIONS_BACKWARD) { | ||||
|             this._select(this._previous()); | ||||
|         } else if (keysym == Clutter.q) { | ||||
|         } else if (keysym === Clutter.KEY_q) { | ||||
|             this._quitApplication(this._selectedIndex); | ||||
|         } else if (this._thumbnailsFocused) { | ||||
|             if (keysym == Clutter.Left) | ||||
|             if (keysym === Clutter.KEY_Left) | ||||
|                 this._select(this._selectedIndex, this._previousWindow()); | ||||
|             else if (keysym == Clutter.Right) | ||||
|             else if (keysym === Clutter.KEY_Right) | ||||
|                 this._select(this._selectedIndex, this._nextWindow()); | ||||
|             else if (keysym == Clutter.Up) | ||||
|             else if (keysym === Clutter.KEY_Up) | ||||
|                 this._select(this._selectedIndex, null, true); | ||||
|             else if (keysym == Clutter.w || keysym == Clutter.F4) | ||||
|             else if (keysym === Clutter.KEY_w || keysym === Clutter.KEY_F4) | ||||
|                 this._closeAppWindow(this._selectedIndex, this._currentWindow); | ||||
|             else | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|         } else if (keysym == Clutter.KEY_Left) { | ||||
|             this._select(this._previous()); | ||||
|         } else if (keysym == Clutter.KEY_Right) { | ||||
|             this._select(this._next()); | ||||
|         } else if (keysym == Clutter.KEY_Down) { | ||||
|             this._select(this._selectedIndex, 0); | ||||
|         } else { | ||||
|             if (keysym == Clutter.Left) | ||||
|                 this._select(this._previous()); | ||||
|             else if (keysym == Clutter.Right) | ||||
|                 this._select(this._next()); | ||||
|             else if (keysym == Clutter.Down) | ||||
|                 this._select(this._selectedIndex, 0); | ||||
|             else | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
| @@ -296,9 +293,9 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup { | ||||
|  | ||||
|     /** | ||||
|      * _select: | ||||
|      * @app: index of the app to select | ||||
|      * @window: (optional) index of which of @app's windows to select | ||||
|      * @forceAppFocus: optional flag, see below | ||||
|      * @param {number} app: index of the app to select | ||||
|      * @param {number=} window: index of which of @app's windows to select | ||||
|      * @param {bool} forceAppFocus: optional flag, see below | ||||
|      * | ||||
|      * Selects the indicated @app, and optional @window, and sets | ||||
|      * this._thumbnailsFocused appropriately to indicate whether the | ||||
| @@ -368,15 +365,15 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup { | ||||
|             onComplete: () => { | ||||
|                 thumbnailsActor.destroy(); | ||||
|                 this.thumbnailsVisible = false; | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|         this._thumbnails = null; | ||||
|         if (this._switcherList._items[this._selectedIndex]) | ||||
|             this._switcherList._items[this._selectedIndex].remove_accessible_state (Atk.StateType.EXPANDED); | ||||
|             this._switcherList._items[this._selectedIndex].remove_accessible_state(Atk.StateType.EXPANDED); | ||||
|     } | ||||
|  | ||||
|     _createThumbnails() { | ||||
|         this._thumbnails = new ThumbnailList (this._items[this._selectedIndex].cachedWindows); | ||||
|         this._thumbnails = new ThumbnailList(this._items[this._selectedIndex].cachedWindows); | ||||
|         this._thumbnails.connect('item-activated', this._windowActivated.bind(this)); | ||||
|         this._thumbnails.connect('item-entered', this._windowEntered.bind(this)); | ||||
|         this._thumbnails.connect('item-removed', this._windowRemoved.bind(this)); | ||||
| @@ -398,34 +395,33 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup { | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => { | ||||
|                 this.thumbnailsVisible = true; | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|  | ||||
|         this._switcherList._items[this._selectedIndex].add_accessible_state (Atk.StateType.EXPANDED); | ||||
|         this._switcherList._items[this._selectedIndex].add_accessible_state(Atk.StateType.EXPANDED); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| class CyclerHighlight { | ||||
|     constructor() { | ||||
| var CyclerHighlight = GObject.registerClass( | ||||
| class CyclerHighlight extends St.Widget { | ||||
|     _init() { | ||||
|         super._init({ layout_manager: new Clutter.BinLayout() }); | ||||
|         this._window = null; | ||||
|  | ||||
|         this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|  | ||||
|         this._clone = new Clutter.Clone(); | ||||
|         this.actor.add_actor(this._clone); | ||||
|         this.add_actor(this._clone); | ||||
|  | ||||
|         this._highlight = new St.Widget({ style_class: 'cycler-highlight' }); | ||||
|         this.actor.add_actor(this._highlight); | ||||
|         this.add_actor(this._highlight); | ||||
|  | ||||
|         let coordinate = Clutter.BindCoordinate.ALL; | ||||
|         let constraint = new Clutter.BindConstraint({ coordinate: coordinate }); | ||||
|         let constraint = new Clutter.BindConstraint({ coordinate }); | ||||
|         this._clone.bind_property('source', constraint, 'source', 0); | ||||
|  | ||||
|         this.actor.add_constraint(constraint); | ||||
|         this.add_constraint(constraint); | ||||
|  | ||||
|         this.actor.connect('notify::allocation', | ||||
|                            this._onAllocationChanged.bind(this)); | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this.connect('notify::allocation', this._onAllocationChanged.bind(this)); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|     set window(w) { | ||||
| @@ -451,7 +447,7 @@ class CyclerHighlight { | ||||
|             this._highlight.set_size(0, 0); | ||||
|             this._highlight.hide(); | ||||
|         } else { | ||||
|             let [x, y] = this.actor.allocation.get_origin(); | ||||
|             let [x, y] = this.allocation.get_origin(); | ||||
|             let rect = this._window.get_frame_rect(); | ||||
|             this._highlight.set_size(rect.width, rect.height); | ||||
|             this._highlight.set_position(rect.x - x, rect.y - y); | ||||
| @@ -462,7 +458,7 @@ class CyclerHighlight { | ||||
|     _onDestroy() { | ||||
|         this.window = null; | ||||
|     } | ||||
| } | ||||
| }); | ||||
|  | ||||
| // We don't show an actual popup, so just provide what SwitcherPopup | ||||
| // expects instead of inheriting from SwitcherList | ||||
| @@ -478,7 +474,7 @@ var CyclerList = GObject.registerClass({ | ||||
| }); | ||||
|  | ||||
| var CyclerPopup = GObject.registerClass({ | ||||
|     GTypeFlags: GObject.TypeFlags.ABSTRACT | ||||
|     GTypeFlags: GObject.TypeFlags.ABSTRACT, | ||||
| }, class CyclerPopup extends SwitcherPopup.SwitcherPopup { | ||||
|     _init() { | ||||
|         super._init(); | ||||
| @@ -489,7 +485,7 @@ var CyclerPopup = GObject.registerClass({ | ||||
|             return; | ||||
|  | ||||
|         this._highlight = new CyclerHighlight(); | ||||
|         global.window_group.add_actor(this._highlight.actor); | ||||
|         global.window_group.add_actor(this._highlight); | ||||
|  | ||||
|         this._switcherList = new CyclerList(); | ||||
|         this._switcherList.connect('item-highlighted', (list, index) => { | ||||
| @@ -499,7 +495,7 @@ var CyclerPopup = GObject.registerClass({ | ||||
|  | ||||
|     _highlightItem(index, _justOutline) { | ||||
|         this._highlight.window = this._items[index]; | ||||
|         global.window_group.set_child_above_sibling(this._highlight.actor, null); | ||||
|         global.window_group.set_child_above_sibling(this._highlight, null); | ||||
|     } | ||||
|  | ||||
|     _finish() { | ||||
| @@ -529,7 +525,7 @@ var CyclerPopup = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
|         this._highlight.actor.destroy(); | ||||
|         this._highlight.destroy(); | ||||
|  | ||||
|         super._onDestroy(); | ||||
|     } | ||||
| @@ -592,20 +588,18 @@ class WindowSwitcherPopup extends SwitcherPopup.SwitcherPopup { | ||||
|     } | ||||
|  | ||||
|     _keyPressHandler(keysym, action) { | ||||
|         if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) { | ||||
|         if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) | ||||
|             this._select(this._next()); | ||||
|         } else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD) { | ||||
|         else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD) | ||||
|             this._select(this._previous()); | ||||
|         } else { | ||||
|             if (keysym == Clutter.Left) | ||||
|                 this._select(this._previous()); | ||||
|             else if (keysym == Clutter.Right) | ||||
|                 this._select(this._next()); | ||||
|             else if (keysym == Clutter.w || keysym == Clutter.F4) | ||||
|                 this._closeWindow(this._selectedIndex); | ||||
|             else | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|         } | ||||
|         else if (keysym == Clutter.KEY_Left) | ||||
|             this._select(this._previous()); | ||||
|         else if (keysym == Clutter.KEY_Right) | ||||
|             this._select(this._next()); | ||||
|         else if (keysym == Clutter.KEY_w || keysym == Clutter.KEY_F4) | ||||
|             this._closeWindow(this._selectedIndex); | ||||
|         else | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| @@ -648,20 +642,22 @@ class WindowCyclerPopup extends CyclerPopup { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var AppIcon = GObject.registerClass({ | ||||
|     GTypeName: 'AltTab_AppIcon' | ||||
| }, class AppIcon extends St.BoxLayout { | ||||
| var AppIcon = GObject.registerClass( | ||||
| class AppIcon extends St.BoxLayout { | ||||
|     _init(app) { | ||||
|         super._init({ style_class: 'alt-tab-app', | ||||
|                       vertical: true }); | ||||
|  | ||||
|         this.app = app; | ||||
|         this.icon = null; | ||||
|         this._iconBin = new St.Bin({ x_fill: true, y_fill: true }); | ||||
|         this._iconBin = new St.Bin(); | ||||
|  | ||||
|         this.add(this._iconBin, { x_fill: false, y_fill: false } ); | ||||
|         this.label = new St.Label({ text: this.app.get_name() }); | ||||
|         this.add(this.label, { x_fill: false }); | ||||
|         this.add_child(this._iconBin); | ||||
|         this.label = new St.Label({ | ||||
|             text: this.app.get_name(), | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         this.add_child(this.label); | ||||
|     } | ||||
|  | ||||
|     // eslint-disable-next-line camelcase | ||||
| @@ -697,7 +693,7 @@ class AppSwitcher extends SwitcherPopup.SwitcherList { | ||||
|             // Cache the window list now; we don't handle dynamic changes here, | ||||
|             // and we don't want to be continually retrieving it | ||||
|             appIcon.cachedWindows = allWindows.filter( | ||||
|                 w => windowTracker.get_window_app (w) == appIcon.app | ||||
|                 w => windowTracker.get_window_app(w) == appIcon.app | ||||
|             ); | ||||
|             if (appIcon.cachedWindows.length > 0) | ||||
|                 this._addIcon(appIcon); | ||||
| @@ -721,9 +717,9 @@ class AppSwitcher extends SwitcherPopup.SwitcherList { | ||||
|  | ||||
|     _setIconSize() { | ||||
|         let j = 0; | ||||
|         while (this._items.length > 1 && this._items[j].style_class != 'item-box') { | ||||
|         while (this._items.length > 1 && this._items[j].style_class != 'item-box') | ||||
|             j++; | ||||
|         } | ||||
|  | ||||
|         let themeNode = this._items[j].get_theme_node(); | ||||
|         this._list.ensure_style(); | ||||
|  | ||||
| @@ -857,7 +853,7 @@ class AppSwitcher extends SwitcherPopup.SwitcherList { | ||||
|         if (appIcon.cachedWindows.length == 1) | ||||
|             arrow.hide(); | ||||
|         else | ||||
|             item.add_accessible_state (Atk.StateType.EXPANDABLE); | ||||
|             item.add_accessible_state(Atk.StateType.EXPANDABLE); | ||||
|     } | ||||
|  | ||||
|     _removeIcon(app) { | ||||
| @@ -893,12 +889,13 @@ class ThumbnailList extends SwitcherPopup.SwitcherList { | ||||
|  | ||||
|             let title = windows[i].get_title(); | ||||
|             if (title) { | ||||
|                 let name = new St.Label({ text: title }); | ||||
|                 // St.Label doesn't support text-align so use a Bin | ||||
|                 let bin = new St.Bin({ x_align: St.Align.MIDDLE }); | ||||
|                 this._labels.push(bin); | ||||
|                 bin.add_actor(name); | ||||
|                 box.add_actor(bin); | ||||
|                 let name = new St.Label({ | ||||
|                     text: title, | ||||
|                     // St.Label doesn't support text-align | ||||
|                     x_align: Clutter.ActorAlign.CENTER, | ||||
|                 }); | ||||
|                 this._labels.push(name); | ||||
|                 box.add_actor(name); | ||||
|  | ||||
|                 this.addItem(box, name); | ||||
|             } else { | ||||
| @@ -977,7 +974,7 @@ class WindowIcon extends St.BoxLayout { | ||||
|  | ||||
|         this._icon = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|  | ||||
|         this.add(this._icon, { x_fill: false, y_fill: false } ); | ||||
|         this.add_child(this._icon); | ||||
|         this.label = new St.Label({ text: window.get_title() }); | ||||
|  | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
| @@ -1000,9 +997,10 @@ class WindowIcon extends St.BoxLayout { | ||||
|             size = WINDOW_PREVIEW_SIZE; | ||||
|             this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor)); | ||||
|  | ||||
|             if (this.app) | ||||
|             if (this.app) { | ||||
|                 this._icon.add_actor(this._createAppIcon(this.app, | ||||
|                                                          APP_ICON_SIZE_SMALL)); | ||||
|             } | ||||
|             break; | ||||
|  | ||||
|         case AppIconMode.APP_ICON_ONLY: | ||||
|   | ||||
| @@ -1,19 +1,20 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported Animation, AnimatedIcon, Spinner */ | ||||
|  | ||||
| const { Clutter, GLib, Gio, St } = imports.gi; | ||||
| const { Clutter, GLib, GObject, Gio, St } = imports.gi; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| var ANIMATED_ICON_UPDATE_TIMEOUT = 16; | ||||
| var SPINNER_ANIMATION_TIME = 300; | ||||
| var SPINNER_ANIMATION_DELAY = 1000; | ||||
|  | ||||
| var Animation = class { | ||||
|     constructor(file, width, height, speed) { | ||||
|         this.actor = new St.Bin(); | ||||
|         this.actor.set_size(width, height); | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this.actor.connect('notify::size', this._syncAnimationSize.bind(this)); | ||||
|         this.actor.connect('resource-scale-changed', | ||||
| var Animation = GObject.registerClass( | ||||
| class Animation extends St.Bin { | ||||
|     _init(file, width, height, speed) { | ||||
|         super._init({ width, height }); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this.connect('resource-scale-changed', | ||||
|             this._loadFile.bind(this, file, width, height)); | ||||
|  | ||||
|         let themeContext = St.ThemeContext.get_for_stage(global.stage); | ||||
| @@ -52,14 +53,14 @@ var Animation = class { | ||||
|     } | ||||
|  | ||||
|     _loadFile(file, width, height) { | ||||
|         let [validResourceScale, resourceScale] = this.actor.get_resource_scale(); | ||||
|         let [validResourceScale, resourceScale] = this.get_resource_scale(); | ||||
|         let wasPlaying = this._isPlaying; | ||||
|  | ||||
|         if (this._isPlaying) | ||||
|             this.stop(); | ||||
|  | ||||
|         this._isLoaded = false; | ||||
|         this.actor.destroy_all_children(); | ||||
|         this.destroy_all_children(); | ||||
|  | ||||
|         if (!validResourceScale) { | ||||
|             if (wasPlaying) | ||||
| @@ -72,7 +73,11 @@ var Animation = class { | ||||
|         this._animations = textureCache.load_sliced_image(file, width, height, | ||||
|                                                           scaleFactor, resourceScale, | ||||
|                                                           this._animationsLoaded.bind(this)); | ||||
|         this.actor.set_child(this._animations); | ||||
|         this._animations.set({ | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         this.set_child(this._animations); | ||||
|  | ||||
|         if (wasPlaying) | ||||
|             this.play(); | ||||
| @@ -83,7 +88,7 @@ var Animation = class { | ||||
|         if (oldFrameActor) | ||||
|             oldFrameActor.hide(); | ||||
|  | ||||
|         this._frame = (frame % this._animations.get_n_children()); | ||||
|         this._frame = frame % this._animations.get_n_children(); | ||||
|  | ||||
|         let newFrameActor = this._animations.get_child_at_index(this._frame); | ||||
|         if (newFrameActor) | ||||
| @@ -99,7 +104,7 @@ var Animation = class { | ||||
|         if (!this._isLoaded) | ||||
|             return; | ||||
|  | ||||
|         let [width, height] = this.actor.get_size(); | ||||
|         let [width, height] = this.get_size(); | ||||
|  | ||||
|         for (let i = 0; i < this._animations.get_n_children(); ++i) | ||||
|             this._animations.get_child_at_index(i).set_size(width, height); | ||||
| @@ -122,21 +127,29 @@ var Animation = class { | ||||
|             themeContext.disconnect(this._scaleChangedId); | ||||
|         this._scaleChangedId = 0; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var AnimatedIcon = class extends Animation { | ||||
|     constructor(file, size) { | ||||
|         super(file, size, size, ANIMATED_ICON_UPDATE_TIMEOUT); | ||||
| var AnimatedIcon = GObject.registerClass( | ||||
| class AnimatedIcon extends Animation { | ||||
|     _init(file, size) { | ||||
|         super._init(file, size, size, ANIMATED_ICON_UPDATE_TIMEOUT); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var Spinner = class extends AnimatedIcon { | ||||
|     constructor(size, animate = false) { | ||||
| var Spinner = GObject.registerClass( | ||||
| class Spinner extends AnimatedIcon { | ||||
|     _init(size, params) { | ||||
|         params = Params.parse(params, { | ||||
|             animate: false, | ||||
|             hideOnStop: false, | ||||
|         }); | ||||
|         let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         super(file, size); | ||||
|         super._init(file, size); | ||||
|  | ||||
|         this.actor.opacity = 0; | ||||
|         this._animate = animate; | ||||
|         this.opacity = 0; | ||||
|         this._animate = params.animate; | ||||
|         this._hideOnStop = params.hideOnStop; | ||||
|         this.visible = !this._hideOnStop; | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
| @@ -145,35 +158,43 @@ var Spinner = class extends AnimatedIcon { | ||||
|     } | ||||
|  | ||||
|     play() { | ||||
|         this.actor.remove_all_transitions(); | ||||
|         this.remove_all_transitions(); | ||||
|         this.show(); | ||||
|  | ||||
|         if (this._animate) { | ||||
|             super.play(); | ||||
|             this.actor.ease({ | ||||
|             this.ease({ | ||||
|                 opacity: 255, | ||||
|                 delay: SPINNER_ANIMATION_DELAY, | ||||
|                 duration: SPINNER_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.LINEAR | ||||
|                 mode: Clutter.AnimationMode.LINEAR, | ||||
|             }); | ||||
|         } else { | ||||
|             this.actor.opacity = 255; | ||||
|             this.opacity = 255; | ||||
|             super.play(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     stop() { | ||||
|         this.actor.remove_all_transitions(); | ||||
|         this.remove_all_transitions(); | ||||
|  | ||||
|         if (this._animate) { | ||||
|             this.actor.ease({ | ||||
|             this.ease({ | ||||
|                 opacity: 0, | ||||
|                 duration: SPINNER_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.LINEAR, | ||||
|                 onComplete: () => super.stop() | ||||
|                 onComplete: () => { | ||||
|                     super.stop(); | ||||
|                     if (this._hideOnStop) | ||||
|                         this.hide(); | ||||
|                 }, | ||||
|             }); | ||||
|         } else { | ||||
|             this.actor.opacity = 0; | ||||
|             this.opacity = 0; | ||||
|             super.stop(); | ||||
|  | ||||
|             if (this._hideOnStop) | ||||
|                 this.hide(); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -55,6 +55,7 @@ const RENAMED_DESKTOP_IDS = { | ||||
|     'org.gnome.taquin.desktop': 'org.gnome.Taquin.desktop', | ||||
|     'org.gnome.Weather.Application.desktop': 'org.gnome.Weather.desktop', | ||||
|     'polari.desktop': 'org.gnome.Polari.desktop', | ||||
|     'shotwell.desktop': 'org.gnome.Shotwell.desktop', | ||||
|     'tali.desktop': 'org.gnome.Tali.desktop', | ||||
|     'totem.desktop': 'org.gnome.Totem.desktop', | ||||
|     'evince.desktop': 'org.gnome.Evince.desktop', | ||||
|   | ||||
| @@ -9,13 +9,13 @@ const { loadInterfaceXML } = imports.misc.fileUtils; | ||||
| var AudioDevice = { | ||||
|     HEADPHONES: 1 << 0, | ||||
|     HEADSET:    1 << 1, | ||||
|     MICROPHONE: 1 << 2 | ||||
|     MICROPHONE: 1 << 2, | ||||
| }; | ||||
|  | ||||
| const AudioDeviceSelectionIface = loadInterfaceXML('org.gnome.Shell.AudioDeviceSelection'); | ||||
|  | ||||
| var AudioDeviceSelectionDialog = GObject.registerClass({ | ||||
|     Signals: { 'device-selected': { param_types: [GObject.TYPE_UINT] } } | ||||
|     Signals: { 'device-selected': { param_types: [GObject.TYPE_UINT] } }, | ||||
| }, class AudioDeviceSelectionDialog extends ModalDialog.ModalDialog { | ||||
|     _init(devices) { | ||||
|         super._init({ styleClass: 'audio-device-selection-dialog' }); | ||||
| @@ -43,15 +43,19 @@ var AudioDeviceSelectionDialog = GObject.registerClass({ | ||||
|         this.contentLayout.style_class = 'audio-selection-content'; | ||||
|         this.contentLayout.add(title); | ||||
|  | ||||
|         this._selectionBox = new St.BoxLayout({ style_class: 'audio-selection-box' }); | ||||
|         this.contentLayout.add(this._selectionBox, { expand: true }); | ||||
|         this._selectionBox = new St.BoxLayout({ | ||||
|             style_class: 'audio-selection-box', | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         this.contentLayout.add_child(this._selectionBox); | ||||
|  | ||||
|         if (Main.sessionMode.allowSettings) | ||||
|         if (Main.sessionMode.allowSettings) { | ||||
|             this.addButton({ action: this._openSettings.bind(this), | ||||
|                              label: _("Sound Settings") }); | ||||
|         } | ||||
|         this.addButton({ action: this.close.bind(this), | ||||
|                          label: _("Cancel"), | ||||
|                          key: Clutter.Escape }); | ||||
|                          key: Clutter.KEY_Escape }); | ||||
|     } | ||||
|  | ||||
|     _getDeviceLabel(device) { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported SystemBackground */ | ||||
|  | ||||
| // READ THIS FIRST | ||||
| // Background handling is a maze of objects, both objects in this file, and | ||||
| @@ -93,7 +94,7 @@ | ||||
| //     MetaBackgroundImage         MetaBackgroundImage | ||||
| //     MetaBackgroundImage         MetaBackgroundImage | ||||
|  | ||||
| const { Clutter, GDesktopEnums, Gio, GLib, GnomeDesktop, Meta } = imports.gi; | ||||
| const { Clutter, GDesktopEnums, Gio, GLib, GObject, GnomeDesktop, Meta } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| @@ -144,7 +145,7 @@ var BackgroundCache = class BackgroundCache { | ||||
|  | ||||
|         let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null); | ||||
|         monitor.connect('changed', | ||||
|                         (obj, file, otherFile, eventType) => { | ||||
|                         (obj, theFile, otherFile, eventType) => { | ||||
|                             // Ignore CHANGED and CREATED events, since in both cases | ||||
|                             // we'll get a CHANGES_DONE_HINT event when done. | ||||
|                             if (eventType != Gio.FileMonitorEvent.CHANGED && | ||||
| @@ -220,16 +221,17 @@ function getBackgroundCache() { | ||||
|     return _backgroundCache; | ||||
| } | ||||
|  | ||||
| var Background = class Background { | ||||
|     constructor(params) { | ||||
| var Background = GObject.registerClass({ | ||||
|     Signals: { 'loaded': {}, 'bg-changed': {} }, | ||||
| }, class Background extends Meta.Background { | ||||
|     _init(params) { | ||||
|         params = Params.parse(params, { monitorIndex: 0, | ||||
|                                         layoutManager: Main.layoutManager, | ||||
|                                         settings: null, | ||||
|                                         file: null, | ||||
|                                         style: null }); | ||||
|  | ||||
|         this.background = new Meta.Background({ meta_display: global.display }); | ||||
|         this.background._delegate = this; | ||||
|         super._init({ meta_display: global.display }); | ||||
|  | ||||
|         this._settings = params.settings; | ||||
|         this._file = params.file; | ||||
| @@ -262,16 +264,14 @@ var Background = class Background { | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|         this.background = null; | ||||
|  | ||||
|         this._cancellable.cancel(); | ||||
|         this._removeAnimationTimeout(); | ||||
|  | ||||
|         let i; | ||||
|         let keys = Object.keys(this._fileWatches); | ||||
|         for (i = 0; i < keys.length; i++) { | ||||
|         for (i = 0; i < keys.length; i++) | ||||
|             this._cache.disconnect(this._fileWatches[keys[i]]); | ||||
|         } | ||||
|  | ||||
|         this._fileWatches = null; | ||||
|  | ||||
|         if (this._timezoneChangedId != 0) | ||||
| @@ -300,9 +300,11 @@ var Background = class Background { | ||||
|  | ||||
|         this._changedIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { | ||||
|             this._changedIdleId = 0; | ||||
|             this.emit('changed'); | ||||
|             this.emit('bg-changed'); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|         GLib.Source.set_name_by_id(this._changedIdleId, | ||||
|             '[gnome-shell] Background._emitChangedSignal'); | ||||
|     } | ||||
|  | ||||
|     updateResolution() { | ||||
| @@ -328,7 +330,7 @@ var Background = class Background { | ||||
|             this.emit('loaded'); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|         GLib.Source.set_name_by_id(id, '[gnome-shell] this.emit'); | ||||
|         GLib.Source.set_name_by_id(id, '[gnome-shell] Background._setLoaded Idle'); | ||||
|     } | ||||
|  | ||||
|     _loadPattern() { | ||||
| @@ -342,9 +344,9 @@ var Background = class Background { | ||||
|         let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY); | ||||
|  | ||||
|         if (shadingType == GDesktopEnums.BackgroundShading.SOLID) | ||||
|             this.background.set_color(color); | ||||
|             this.set_color(color); | ||||
|         else | ||||
|             this.background.set_gradient(shadingType, color, secondColor); | ||||
|             this.set_gradient(shadingType, color, secondColor); | ||||
|     } | ||||
|  | ||||
|     _watchFile(file) { | ||||
| @@ -380,13 +382,13 @@ var Background = class Background { | ||||
|         let finish = () => { | ||||
|             this._setLoaded(); | ||||
|             if (files.length > 1) { | ||||
|                 this.background.set_blend(files[0], files[1], | ||||
|                                           this._animation.transitionProgress, | ||||
|                                           this._style); | ||||
|                 this.set_blend(files[0], files[1], | ||||
|                                this._animation.transitionProgress, | ||||
|                                this._style); | ||||
|             } else if (files.length > 0) { | ||||
|                 this.background.set_file(files[0], this._style); | ||||
|                 this.set_file(files[0], this._style); | ||||
|             } else { | ||||
|                 this.background.set_file(null, this._style); | ||||
|                 this.set_file(null, this._style); | ||||
|             } | ||||
|             this._queueUpdateAnimation(); | ||||
|         }; | ||||
| @@ -401,6 +403,7 @@ var Background = class Background { | ||||
|                 if (numPendingImages == 0) | ||||
|                     finish(); | ||||
|             } else { | ||||
|                 // eslint-disable-next-line no-loop-func | ||||
|                 let id = image.connect('loaded', () => { | ||||
|                     image.disconnect(id); | ||||
|                     numPendingImages--; | ||||
| @@ -442,7 +445,7 @@ var Background = class Background { | ||||
|  | ||||
|     _loadAnimation(file) { | ||||
|         this._cache.getAnimation({ | ||||
|             file: file, | ||||
|             file, | ||||
|             settingsSchema: this._settings.schema_id, | ||||
|             onLoaded: animation => { | ||||
|                 this._animation = animation; | ||||
| @@ -454,12 +457,12 @@ var Background = class Background { | ||||
|  | ||||
|                 this._updateAnimation(); | ||||
|                 this._watchFile(file); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _loadImage(file) { | ||||
|         this.background.set_file(file, this._style); | ||||
|         this.set_file(file, this._style); | ||||
|         this._watchFile(file); | ||||
|  | ||||
|         let cache = Meta.BackgroundImageCache.get_default(); | ||||
| @@ -493,13 +496,14 @@ var Background = class Background { | ||||
|  | ||||
|         this._loadFile(this._file); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Background.prototype); | ||||
| }); | ||||
|  | ||||
| let _systemBackground; | ||||
|  | ||||
| var SystemBackground = class SystemBackground { | ||||
|     constructor() { | ||||
| var SystemBackground = GObject.registerClass({ | ||||
|     Signals: { 'loaded': {} }, | ||||
| }, class SystemBackground extends Meta.BackgroundActor { | ||||
|     _init() { | ||||
|         let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/noise-texture.png'); | ||||
|  | ||||
|         if (_systemBackground == null) { | ||||
| @@ -508,9 +512,11 @@ var SystemBackground = class SystemBackground { | ||||
|             _systemBackground.set_file(file, GDesktopEnums.BackgroundStyle.WALLPAPER); | ||||
|         } | ||||
|  | ||||
|         this.actor = new Meta.BackgroundActor({ meta_display: global.display, | ||||
|                                                 monitor: 0, | ||||
|                                                 background: _systemBackground }); | ||||
|         super._init({ | ||||
|             meta_display: global.display, | ||||
|             monitor: 0, | ||||
|             background: _systemBackground, | ||||
|         }); | ||||
|  | ||||
|         let cache = Meta.BackgroundImageCache.get_default(); | ||||
|         let image = cache.load(file); | ||||
| @@ -529,8 +535,7 @@ var SystemBackground = class SystemBackground { | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(SystemBackground.prototype); | ||||
| }); | ||||
|  | ||||
| var BackgroundSource = class BackgroundSource { | ||||
|     constructor(layoutManager, settingsSchema) { | ||||
| @@ -566,7 +571,7 @@ var BackgroundSource = class BackgroundSource { | ||||
|  | ||||
|         // We don't watch changes to settings here, | ||||
|         // instead we rely on Background to watch those | ||||
|         // and emit 'changed' at the right time | ||||
|         // and emit 'bg-changed' at the right time | ||||
|  | ||||
|         if (this._overrideImage != null) { | ||||
|             file = Gio.File.new_for_path(this._overrideImage); | ||||
| @@ -588,14 +593,14 @@ var BackgroundSource = class BackgroundSource { | ||||
|  | ||||
|         if (!(monitorIndex in this._backgrounds)) { | ||||
|             let background = new Background({ | ||||
|                 monitorIndex: monitorIndex, | ||||
|                 monitorIndex, | ||||
|                 layoutManager: this._layoutManager, | ||||
|                 settings: this._settings, | ||||
|                 file: file, | ||||
|                 style: style | ||||
|                 file, | ||||
|                 style, | ||||
|             }); | ||||
|  | ||||
|             background._changedId = background.connect('changed', () => { | ||||
|             background._changedId = background.connect('bg-changed', () => { | ||||
|                 background.disconnect(background._changedId); | ||||
|                 background.destroy(); | ||||
|                 delete this._backgrounds[monitorIndex]; | ||||
| @@ -621,11 +626,11 @@ var BackgroundSource = class BackgroundSource { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var Animation = class Animation { | ||||
|     constructor(params) { | ||||
|         params = Params.parse(params, { file: null }); | ||||
| var Animation = GObject.registerClass( | ||||
| class Animation extends GnomeDesktop.BGSlideShow { | ||||
|     _init(params) { | ||||
|         super._init(params); | ||||
|  | ||||
|         this.file = params.file; | ||||
|         this.keyFrameFiles = []; | ||||
|         this.transitionProgress = 0.0; | ||||
|         this.transitionDuration = 0.0; | ||||
| @@ -633,9 +638,7 @@ var Animation = class Animation { | ||||
|     } | ||||
|  | ||||
|     load(callback) { | ||||
|         this._show = new GnomeDesktop.BGSlideShow({ file: this.file }); | ||||
|  | ||||
|         this._show.load_async(null, () => { | ||||
|         this.load_async(null, () => { | ||||
|             this.loaded = true; | ||||
|             if (callback) | ||||
|                 callback(); | ||||
| @@ -645,13 +648,11 @@ var Animation = class Animation { | ||||
|     update(monitor) { | ||||
|         this.keyFrameFiles = []; | ||||
|  | ||||
|         if (!this._show) | ||||
|         if (this.get_num_slides() < 1) | ||||
|             return; | ||||
|  | ||||
|         if (this._show.get_num_slides() < 1) | ||||
|             return; | ||||
|  | ||||
|         let [progress, duration, isFixed_, filename1, filename2] = this._show.get_current_slide(monitor.width, monitor.height); | ||||
|         let [progress, duration, isFixed_, filename1, filename2] = | ||||
|             this.get_current_slide(monitor.width, monitor.height); | ||||
|  | ||||
|         this.transitionDuration = duration; | ||||
|         this.transitionProgress = progress; | ||||
| @@ -662,8 +663,7 @@ var Animation = class Animation { | ||||
|         if (filename2) | ||||
|             this.keyFrameFiles.push(Gio.File.new_for_path(filename2)); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Animation.prototype); | ||||
| }); | ||||
|  | ||||
| var BackgroundManager = class BackgroundManager { | ||||
|     constructor(params) { | ||||
| @@ -714,7 +714,7 @@ var BackgroundManager = class BackgroundManager { | ||||
|             opacity: 0, | ||||
|             duration: FADE_ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => oldBackgroundActor.destroy() | ||||
|             onComplete: () => oldBackgroundActor.destroy(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -732,7 +732,7 @@ var BackgroundManager = class BackgroundManager { | ||||
|  | ||||
|         this._newBackgroundActor = newBackgroundActor; | ||||
|  | ||||
|         let background = newBackgroundActor.background._delegate; | ||||
|         let background = newBackgroundActor.background; | ||||
|  | ||||
|         if (background.isLoaded) { | ||||
|             this._swapBackgroundActor(); | ||||
| @@ -752,7 +752,7 @@ var BackgroundManager = class BackgroundManager { | ||||
|         let backgroundActor = new Meta.BackgroundActor({ | ||||
|             meta_display: global.display, | ||||
|             monitor: this._monitorIndex, | ||||
|             background: background.background, | ||||
|             background, | ||||
|             vignette: this._vignette, | ||||
|             vignette_sharpness: 0.5, | ||||
|             brightness: 0.5, | ||||
| @@ -763,10 +763,10 @@ var BackgroundManager = class BackgroundManager { | ||||
|         if (this._controlPosition) { | ||||
|             let monitor = this._layoutManager.monitors[this._monitorIndex]; | ||||
|             backgroundActor.set_position(monitor.x, monitor.y); | ||||
|             backgroundActor.lower_bottom(); | ||||
|             this._container.set_child_below_sibling(backgroundActor, null); | ||||
|         } | ||||
|  | ||||
|         let changeSignalId = background.connect('changed', () => { | ||||
|         let changeSignalId = background.connect('bg-changed', () => { | ||||
|             background.disconnect(changeSignalId); | ||||
|             changeSignalId = null; | ||||
|             this._updateBackgroundActor(); | ||||
|   | ||||
| @@ -35,11 +35,12 @@ function addBackgroundMenu(actor, layoutManager) { | ||||
|     } | ||||
|  | ||||
|     let clickAction = new Clutter.ClickAction(); | ||||
|     clickAction.connect('long-press', (action, actor, state) => { | ||||
|         if (state == Clutter.LongPressState.QUERY) | ||||
|             return ((action.get_button() == 0 || | ||||
|     clickAction.connect('long-press', (action, theActor, state) => { | ||||
|         if (state == Clutter.LongPressState.QUERY) { | ||||
|             return (action.get_button() == 0 || | ||||
|                      action.get_button() == 1) && | ||||
|                     !actor._backgroundMenu.isOpen); | ||||
|                     !actor._backgroundMenu.isOpen; | ||||
|         } | ||||
|         if (state == Clutter.LongPressState.ACTIVATE) { | ||||
|             let [x, y] = action.get_coords(); | ||||
|             openMenu(x, y); | ||||
|   | ||||
| @@ -16,8 +16,8 @@ var BarLevel = GObject.registerClass({ | ||||
|         'overdrive-start': GObject.ParamSpec.double( | ||||
|             'overdrive-start', 'overdrive-start', 'overdrive-start', | ||||
|             GObject.ParamFlags.READWRITE, | ||||
|             1, 2, 1) | ||||
|     } | ||||
|             1, 2, 1), | ||||
|     }, | ||||
| }, class BarLevel extends St.DrawingArea { | ||||
|     _init(params) { | ||||
|         this._maxValue = 1; | ||||
| @@ -27,7 +27,7 @@ var BarLevel = GObject.registerClass({ | ||||
|  | ||||
|         let defaultParams = { | ||||
|             style_class: 'barlevel', | ||||
|             accessible_role: Atk.Role.LEVEL_BAR | ||||
|             accessible_role: Atk.Role.LEVEL_BAR, | ||||
|         }; | ||||
|         super._init(Object.assign(defaultParams, params)); | ||||
|         this.connect('allocation-changed', (actor, box) => { | ||||
| @@ -88,9 +88,10 @@ var BarLevel = GObject.registerClass({ | ||||
|         if (this._overdriveStart == value) | ||||
|             return; | ||||
|  | ||||
|         if (value > this._maxValue) | ||||
|         if (value > this._maxValue) { | ||||
|             throw new Error(`Tried to set overdrive value to ${value}, ` + | ||||
|                 `which is a number greater than the maximum allowed value ${this._maxValue}`); | ||||
|         } | ||||
|  | ||||
|         this._overdriveStart = value; | ||||
|         this.notify('overdrive-start'); | ||||
|   | ||||
| @@ -44,14 +44,20 @@ var BoxPointer = GObject.registerClass({ | ||||
|         this._border = new St.DrawingArea(); | ||||
|         this._border.connect('repaint', this._drawBorder.bind(this)); | ||||
|         this.add_actor(this._border); | ||||
|         this.bin.raise(this._border); | ||||
|         this.set_child_above_sibling(this.bin, this._border); | ||||
|         this._sourceAlignment = 0.5; | ||||
|         this._capturedEventId = 0; | ||||
|         this._muteInput(); | ||||
|         this._muteInput = true; | ||||
|  | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|     vfunc_captured_event() { | ||||
|         if (this._muteInput) | ||||
|             return Clutter.EVENT_STOP; | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
|         if (this._sourceActorDestroyId) { | ||||
|             this._sourceActor.disconnect(this._sourceActorDestroyId); | ||||
| @@ -63,23 +69,10 @@ var BoxPointer = GObject.registerClass({ | ||||
|         return this._arrowSide; | ||||
|     } | ||||
|  | ||||
|     _muteInput() { | ||||
|         if (this._capturedEventId == 0) | ||||
|             this._capturedEventId = this.connect('captured-event', | ||||
|                                                  () => Clutter.EVENT_STOP); | ||||
|     } | ||||
|  | ||||
|     _unmuteInput() { | ||||
|         if (this._capturedEventId != 0) { | ||||
|             this.disconnect(this._capturedEventId); | ||||
|             this._capturedEventId = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     open(animate, onComplete) { | ||||
|         let themeNode = this.get_theme_node(); | ||||
|         let rise = themeNode.get_length('-arrow-rise'); | ||||
|         let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0; | ||||
|         let animationTime = animate & PopupAnimation.FULL ? POPUP_ANIMATION_TIME : 0; | ||||
|  | ||||
|         if (animate & PopupAnimation.FADE) | ||||
|             this.opacity = 0; | ||||
| @@ -112,10 +105,10 @@ var BoxPointer = GObject.registerClass({ | ||||
|             duration: animationTime, | ||||
|             mode: Clutter.AnimationMode.LINEAR, | ||||
|             onComplete: () => { | ||||
|                 this._unmuteInput(); | ||||
|                 this._muteInput = false; | ||||
|                 if (onComplete) | ||||
|                     onComplete(); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -127,8 +120,8 @@ var BoxPointer = GObject.registerClass({ | ||||
|         let translationY = 0; | ||||
|         let themeNode = this.get_theme_node(); | ||||
|         let rise = themeNode.get_length('-arrow-rise'); | ||||
|         let fade = (animate & PopupAnimation.FADE); | ||||
|         let animationTime = (animate & PopupAnimation.FULL) ? POPUP_ANIMATION_TIME : 0; | ||||
|         let fade = animate & PopupAnimation.FADE; | ||||
|         let animationTime = animate & PopupAnimation.FULL ? POPUP_ANIMATION_TIME : 0; | ||||
|  | ||||
|         if (animate & PopupAnimation.SLIDE) { | ||||
|             switch (this._arrowSide) { | ||||
| @@ -147,7 +140,7 @@ var BoxPointer = GObject.registerClass({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this._muteInput(); | ||||
|         this._muteInput = true; | ||||
|  | ||||
|         this.remove_all_transitions(); | ||||
|         this.ease({ | ||||
| @@ -163,7 +156,7 @@ var BoxPointer = GObject.registerClass({ | ||||
|                 this.translation_y = 0; | ||||
|                 if (onComplete) | ||||
|                     onComplete(); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -254,11 +247,10 @@ var BoxPointer = GObject.registerClass({ | ||||
|             let [absX, absY] = this.get_transformed_position(); | ||||
|  | ||||
|             if (this._arrowSide == St.Side.TOP || | ||||
|                 this._arrowSide == St.Side.BOTTOM) { | ||||
|                 this._arrowSide == St.Side.BOTTOM) | ||||
|                 this._arrowOrigin = sourceX - absX + sourceWidth / 2; | ||||
|             } else { | ||||
|             else | ||||
|                 this._arrowOrigin = sourceY - absY + sourceHeight / 2; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let borderWidth = themeNode.get_length('-arrow-border-width'); | ||||
| @@ -273,20 +265,19 @@ var BoxPointer = GObject.registerClass({ | ||||
|  | ||||
|         let [width, height] = area.get_surface_size(); | ||||
|         let [boxWidth, boxHeight] = [width, height]; | ||||
|         if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM) { | ||||
|         if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM) | ||||
|             boxHeight -= rise; | ||||
|         } else { | ||||
|         else | ||||
|             boxWidth -= rise; | ||||
|         } | ||||
|  | ||||
|         let cr = area.get_context(); | ||||
|  | ||||
|         // Translate so that box goes from 0,0 to boxWidth,boxHeight, | ||||
|         // with the arrow poking out of that | ||||
|         if (this._arrowSide == St.Side.TOP) { | ||||
|         if (this._arrowSide == St.Side.TOP) | ||||
|             cr.translate(0, rise); | ||||
|         } else if (this._arrowSide == St.Side.LEFT) { | ||||
|         else if (this._arrowSide == St.Side.LEFT) | ||||
|             cr.translate(rise, 0); | ||||
|         } | ||||
|  | ||||
|         let [x1, y1] = [halfBorder, halfBorder]; | ||||
|         let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder]; | ||||
| @@ -482,7 +473,7 @@ var BoxPointer = GObject.registerClass({ | ||||
|         let borderWidth = themeNode.get_length('-arrow-border-width'); | ||||
|         let arrowBase = themeNode.get_length('-arrow-base'); | ||||
|         let borderRadius = themeNode.get_length('-arrow-border-radius'); | ||||
|         let margin = (4 * borderRadius + borderWidth + arrowBase); | ||||
|         let margin = 4 * borderRadius + borderWidth + arrowBase; | ||||
|  | ||||
|         let gap = themeNode.get_length('-boxpointer-gap'); | ||||
|         let padding = themeNode.get_length('-arrow-rise'); | ||||
| @@ -533,11 +524,11 @@ var BoxPointer = GObject.registerClass({ | ||||
|             arrowOrigin = sourceCenterX - resX; | ||||
|             if (arrowOrigin <= (x1 + (borderRadius + halfBase))) { | ||||
|                 if (arrowOrigin > x1) | ||||
|                     resX += (arrowOrigin - x1); | ||||
|                     resX += arrowOrigin - x1; | ||||
|                 arrowOrigin = x1; | ||||
|             } else if (arrowOrigin >= (x2 - (borderRadius + halfBase))) { | ||||
|                 if (arrowOrigin < x2) | ||||
|                     resX -= (x2 - arrowOrigin); | ||||
|                     resX -= x2 - arrowOrigin; | ||||
|                 arrowOrigin = x2; | ||||
|             } | ||||
|             break; | ||||
| @@ -552,11 +543,11 @@ var BoxPointer = GObject.registerClass({ | ||||
|             arrowOrigin = sourceCenterY - resY; | ||||
|             if (arrowOrigin <= (y1 + (borderRadius + halfBase))) { | ||||
|                 if (arrowOrigin > y1) | ||||
|                     resY += (arrowOrigin - y1); | ||||
|                     resY += arrowOrigin - y1; | ||||
|                 arrowOrigin = y1; | ||||
|             } else if (arrowOrigin >= (y2 - (borderRadius + halfBase))) { | ||||
|                 if (arrowOrigin < y2) | ||||
|                     resX -= (y2 - arrowOrigin); | ||||
|                     resX -= y2 - arrowOrigin; | ||||
|                 arrowOrigin = y2; | ||||
|             } | ||||
|             break; | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported Calendar, CalendarMessageList */ | ||||
| /* exported Calendar, CalendarMessageList, DBusEventSource */ | ||||
|  | ||||
| const { Clutter, Gio, GLib, GObject, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const MessageList = imports.ui.messageList; | ||||
| @@ -21,7 +20,7 @@ var MESSAGE_ICON_SIZE = -1; // pick up from CSS | ||||
| var NC_ = (context, str) => `${context}\u0004${str}`; | ||||
|  | ||||
| function sameYear(dateA, dateB) { | ||||
|     return (dateA.getYear() == dateB.getYear()); | ||||
|     return dateA.getYear() == dateB.getYear(); | ||||
| } | ||||
|  | ||||
| function sameMonth(dateA, dateB) { | ||||
| @@ -79,7 +78,7 @@ function _getCalendarDayAbbreviation(dayNumber) { | ||||
|         /* Translators: Calendar grid abbreviation for Friday */ | ||||
|         NC_("grid friday", "F"), | ||||
|         /* Translators: Calendar grid abbreviation for Saturday */ | ||||
|         NC_("grid saturday", "S") | ||||
|         NC_("grid saturday", "S"), | ||||
|     ]; | ||||
|     return Shell.util_translate_time_string(abbreviations[dayNumber]); | ||||
| } | ||||
| @@ -99,17 +98,54 @@ var CalendarEvent = class CalendarEvent { | ||||
| // Interface for appointments/events - e.g. the contents of a calendar | ||||
| // | ||||
|  | ||||
| // First, an implementation with no events | ||||
| var EmptyEventSource = class EmptyEventSource { | ||||
|     constructor() { | ||||
|         this.isLoading = false; | ||||
|         this.isDummy = true; | ||||
|         this.hasCalendars = false; | ||||
| var EventSourceBase = GObject.registerClass({ | ||||
|     GTypeFlags: GObject.TypeFlags.ABSTRACT, | ||||
|     Properties: { | ||||
|         'has-calendars': GObject.ParamSpec.boolean( | ||||
|             'has-calendars', 'has-calendars', 'has-calendars', | ||||
|             GObject.ParamFlags.READABLE, | ||||
|             false), | ||||
|         'is-loading': GObject.ParamSpec.boolean( | ||||
|             'is-loading', 'is-loading', 'is-loading', | ||||
|             GObject.ParamFlags.READABLE, | ||||
|             false), | ||||
|     }, | ||||
|     Signals: { 'changed': {} }, | ||||
| }, class EventSourceBase extends GObject.Object { | ||||
|     get isLoading() { | ||||
|         throw new GObject.NotImplementedError(`isLoading in ${this.constructor.name}`); | ||||
|     } | ||||
|  | ||||
|     get hasCalendars() { | ||||
|         throw new GObject.NotImplementedError(`hasCalendars in ${this.constructor.name}`); | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|     } | ||||
|  | ||||
|     requestRange(_begin, _end) { | ||||
|         throw new GObject.NotImplementedError(`requestRange in ${this.constructor.name}`); | ||||
|     } | ||||
|  | ||||
|     getEvents(_begin, _end) { | ||||
|         throw new GObject.NotImplementedError(`getEvents in ${this.constructor.name}`); | ||||
|     } | ||||
|  | ||||
|     hasEvents(_day) { | ||||
|         throw new GObject.NotImplementedError(`hasEvents in ${this.constructor.name}`); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var EmptyEventSource = GObject.registerClass( | ||||
| class EmptyEventSource extends EventSourceBase { | ||||
|     get isLoading() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     get hasCalendars() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     requestRange(_begin, _end) { | ||||
|     } | ||||
|  | ||||
| @@ -121,8 +157,7 @@ var EmptyEventSource = class EmptyEventSource { | ||||
|     hasEvents(_day) { | ||||
|         return false; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(EmptyEventSource.prototype); | ||||
| }); | ||||
|  | ||||
| const CalendarServerIface = loadInterfaceXML('org.gnome.Shell.CalendarServer'); | ||||
|  | ||||
| @@ -154,11 +189,12 @@ function _dateIntervalsOverlap(a0, a1, b0, b1) { | ||||
| } | ||||
|  | ||||
| // an implementation that reads data from a session bus service | ||||
| var DBusEventSource = class DBusEventSource { | ||||
|     constructor() { | ||||
| var DBusEventSource = GObject.registerClass( | ||||
| class DBusEventSource extends EventSourceBase { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|         this._resetCache(); | ||||
|         this.isLoading = false; | ||||
|         this.isDummy = false; | ||||
|         this._isLoading = false; | ||||
|  | ||||
|         this._initialized = false; | ||||
|         this._dbusProxy = new CalendarServer(); | ||||
| @@ -193,12 +229,12 @@ var DBusEventSource = class DBusEventSource { | ||||
|             }); | ||||
|  | ||||
|             this._dbusProxy.connect('g-properties-changed', () => { | ||||
|                 this.emit('notify::has-calendars'); | ||||
|                 this.notify('has-calendars'); | ||||
|             }); | ||||
|  | ||||
|             this._initialized = loaded; | ||||
|             if (loaded) { | ||||
|                 this.emit('notify::has-calendars'); | ||||
|                 this.notify('has-calendars'); | ||||
|                 this._onNameAppeared(); | ||||
|             } | ||||
|         }); | ||||
| @@ -215,6 +251,10 @@ var DBusEventSource = class DBusEventSource { | ||||
|             return false; | ||||
|     } | ||||
|  | ||||
|     get isLoading() { | ||||
|         return this._isLoading; | ||||
|     } | ||||
|  | ||||
|     _resetCache() { | ||||
|         this._events = []; | ||||
|         this._lastRequestBegin = null; | ||||
| @@ -252,7 +292,7 @@ var DBusEventSource = class DBusEventSource { | ||||
|         newEvents.sort((ev1, ev2) => ev1.date.getTime() - ev2.date.getTime()); | ||||
|  | ||||
|         this._events = newEvents; | ||||
|         this.isLoading = false; | ||||
|         this._isLoading = false; | ||||
|         this.emit('changed'); | ||||
|     } | ||||
|  | ||||
| @@ -272,7 +312,7 @@ var DBusEventSource = class DBusEventSource { | ||||
|  | ||||
|     requestRange(begin, end) { | ||||
|         if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { | ||||
|             this.isLoading = true; | ||||
|             this._isLoading = true; | ||||
|             this._lastRequestBegin = begin; | ||||
|             this._lastRequestEnd = end; | ||||
|             this._curRequestBegin = begin; | ||||
| @@ -286,9 +326,8 @@ var DBusEventSource = class DBusEventSource { | ||||
|         for (let n = 0; n < this._events.length; n++) { | ||||
|             let event = this._events[n]; | ||||
|  | ||||
|             if (_dateIntervalsOverlap (event.date, event.end, begin, end)) { | ||||
|             if (_dateIntervalsOverlap(event.date, event.end, begin, end)) | ||||
|                 result.push(event); | ||||
|             } | ||||
|         } | ||||
|         result.sort((event1, event2) => { | ||||
|             // sort events by end time on ending day | ||||
| @@ -310,11 +349,12 @@ var DBusEventSource = class DBusEventSource { | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(DBusEventSource.prototype); | ||||
| }); | ||||
|  | ||||
| var Calendar = class Calendar { | ||||
|     constructor() { | ||||
| var Calendar = GObject.registerClass({ | ||||
|     Signals: { 'selected-date-changed': { param_types: [GLib.DateTime.$gtype] } }, | ||||
| }, class Calendar extends St.Widget { | ||||
|     _init() { | ||||
|         this._weekStart = Shell.util_get_week_start(); | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.calendar' }); | ||||
|  | ||||
| @@ -344,19 +384,19 @@ var Calendar = class Calendar { | ||||
|  | ||||
|         this._shouldDateGrabFocus = false; | ||||
|  | ||||
|         this.actor = new St.Widget({ style_class: 'calendar', | ||||
|                                      layout_manager: new Clutter.TableLayout(), | ||||
|                                      reactive: true }); | ||||
|         super._init({ | ||||
|             style_class: 'calendar', | ||||
|             layout_manager: new Clutter.GridLayout(), | ||||
|             reactive: true, | ||||
|         }); | ||||
|  | ||||
|         this.actor.connect('scroll-event', | ||||
|                            this._onScroll.bind(this)); | ||||
|  | ||||
|         this._buildHeader (); | ||||
|         this._buildHeader(); | ||||
|     } | ||||
|  | ||||
|     // @eventSource: is an object implementing the EventSource API, e.g. the | ||||
|     // requestRange(), getEvents(), hasEvents() methods and the ::changed signal. | ||||
|     setEventSource(eventSource) { | ||||
|         if (!(eventSource instanceof EventSourceBase)) | ||||
|             throw new Error('Event source is not valid type'); | ||||
|  | ||||
|         this._eventSource = eventSource; | ||||
|         this._eventSource.connect('changed', () => { | ||||
|             this._rebuildCalendar(); | ||||
| @@ -373,7 +413,10 @@ var Calendar = class Calendar { | ||||
|  | ||||
|         this._selectedDate = date; | ||||
|         this._update(); | ||||
|         this.emit('selected-date-changed', new Date(this._selectedDate)); | ||||
|  | ||||
|         let datetime = GLib.DateTime.new_from_unix_local( | ||||
|             this._selectedDate.getTime() / 1000); | ||||
|         this.emit('selected-date-changed', datetime); | ||||
|     } | ||||
|  | ||||
|     updateTimeZone() { | ||||
| @@ -384,14 +427,13 @@ var Calendar = class Calendar { | ||||
|     } | ||||
|  | ||||
|     _buildHeader() { | ||||
|         let layout = this.actor.layout_manager; | ||||
|         let layout = this.layout_manager; | ||||
|         let offsetCols = this._useWeekdate ? 1 : 0; | ||||
|         this.actor.destroy_all_children(); | ||||
|         this.destroy_all_children(); | ||||
|  | ||||
|         // Top line of the calendar '<| September 2009 |>' | ||||
|         this._topBox = new St.BoxLayout(); | ||||
|         layout.pack(this._topBox, 0, 0); | ||||
|         layout.set_span(this._topBox, offsetCols + 7, 1); | ||||
|         layout.attach(this._topBox, 0, 0, offsetCols + 7, 1); | ||||
|  | ||||
|         this._backButton = new St.Button({ style_class: 'calendar-change-month-back pager-button', | ||||
|                                            accessible_name: _("Previous month"), | ||||
| @@ -400,9 +442,13 @@ var Calendar = class Calendar { | ||||
|         this._topBox.add(this._backButton); | ||||
|         this._backButton.connect('clicked', this._onPrevMonthButtonClicked.bind(this)); | ||||
|  | ||||
|         this._monthLabel = new St.Label({ style_class: 'calendar-month-label', | ||||
|                                           can_focus: true }); | ||||
|         this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE }); | ||||
|         this._monthLabel = new St.Label({ | ||||
|             style_class: 'calendar-month-label', | ||||
|             can_focus: true, | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         this._topBox.add_child(this._monthLabel); | ||||
|  | ||||
|         this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward pager-button', | ||||
|                                               accessible_name: _("Next month"), | ||||
| @@ -428,20 +474,20 @@ var Calendar = class Calendar { | ||||
|                                        can_focus: true }); | ||||
|             label.accessible_name = iter.toLocaleFormat('%A'); | ||||
|             let col; | ||||
|             if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) | ||||
|             if (this.get_text_direction() == Clutter.TextDirection.RTL) | ||||
|                 col = 6 - (7 + iter.getDay() - this._weekStart) % 7; | ||||
|             else | ||||
|                 col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7; | ||||
|             layout.pack(label, col, 1); | ||||
|             layout.attach(label, col, 1, 1, 1); | ||||
|             iter.setTime(iter.getTime() + MSECS_IN_DAY); | ||||
|         } | ||||
|  | ||||
|         // All the children after this are days, and get removed when we update the calendar | ||||
|         this._firstDayIndex = this.actor.get_n_children(); | ||||
|         this._firstDayIndex = this.get_n_children(); | ||||
|     } | ||||
|  | ||||
|     _onScroll(actor, event) { | ||||
|         switch (event.get_scroll_direction()) { | ||||
|     vfunc_scroll_event(scrollEvent) { | ||||
|         switch (scrollEvent.direction) { | ||||
|         case Clutter.ScrollDirection.UP: | ||||
|         case Clutter.ScrollDirection.LEFT: | ||||
|             this._onPrevMonthButtonClicked(); | ||||
| @@ -511,7 +557,7 @@ var Calendar = class Calendar { | ||||
|         let now = new Date(); | ||||
|  | ||||
|         // Remove everything but the topBox and the weekday labels | ||||
|         let children = this.actor.get_children(); | ||||
|         let children = this.get_children(); | ||||
|         for (let i = this._firstDayIndex; i < children.length; i++) | ||||
|             children[i].destroy(); | ||||
|  | ||||
| @@ -548,7 +594,7 @@ var Calendar = class Calendar { | ||||
|  | ||||
|         beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY); | ||||
|  | ||||
|         let layout = this.actor.layout_manager; | ||||
|         let layout = this.layout_manager; | ||||
|         let iter = new Date(beginDate); | ||||
|         let row = 2; | ||||
|         // nRows here means 6 weeks + one header + one navbar | ||||
| @@ -559,7 +605,7 @@ var Calendar = class Calendar { | ||||
|                                          can_focus: true }); | ||||
|             let rtl = button.get_text_direction() == Clutter.TextDirection.RTL; | ||||
|  | ||||
|             if (this._eventSource.isDummy) | ||||
|             if (this._eventSource instanceof EmptyEventSource) | ||||
|                 button.reactive = false; | ||||
|  | ||||
|             button._date = new Date(iter); | ||||
| @@ -603,7 +649,7 @@ var Calendar = class Calendar { | ||||
|                 col = 6 - (7 + iter.getDay() - this._weekStart) % 7; | ||||
|             else | ||||
|                 col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7; | ||||
|             layout.pack(button, col, row); | ||||
|             layout.attach(button, col, row, 1, 1); | ||||
|  | ||||
|             this._buttons.push(button); | ||||
|  | ||||
| @@ -613,7 +659,7 @@ var Calendar = class Calendar { | ||||
|                                            can_focus: true }); | ||||
|                 let weekFormat = Shell.util_translate_time_string(N_("Week %V")); | ||||
|                 label.accessible_name = iter.toLocaleFormat(weekFormat); | ||||
|                 layout.pack(label, rtl ? 7 : 0, row); | ||||
|                 layout.attach(label, rtl ? 7 : 0, row, 1, 1); | ||||
|             } | ||||
|  | ||||
|             iter.setTime(iter.getTime() + MSECS_IN_DAY); | ||||
| @@ -648,12 +694,12 @@ var Calendar = class Calendar { | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Calendar.prototype); | ||||
| }); | ||||
|  | ||||
| var EventMessage = class EventMessage extends MessageList.Message { | ||||
|     constructor(event, date) { | ||||
|         super('', event.summary); | ||||
| var EventMessage = GObject.registerClass( | ||||
| class EventMessage extends MessageList.Message { | ||||
|     _init(event, date) { | ||||
|         super._init('', event.summary); | ||||
|  | ||||
|         this._event = event; | ||||
|         this._date = date; | ||||
| @@ -662,18 +708,19 @@ var EventMessage = class EventMessage extends MessageList.Message { | ||||
|  | ||||
|         this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' }); | ||||
|         this.setIcon(this._icon); | ||||
|     } | ||||
|  | ||||
|         this.actor.connect('style-changed', () => { | ||||
|             let iconVisible = this.actor.get_parent().has_style_pseudo_class('first-child'); | ||||
|             this._icon.opacity = (iconVisible ? 255 : 0); | ||||
|         }); | ||||
|     vfunc_style_changed() { | ||||
|         let iconVisible = this.get_parent().has_style_pseudo_class('first-child'); | ||||
|         this._icon.opacity = iconVisible ? 255 : 0; | ||||
|         super.vfunc_style_changed(); | ||||
|     } | ||||
|  | ||||
|     _formatEventTime() { | ||||
|         let periodBegin = _getBeginningOfDay(this._date); | ||||
|         let periodEnd = _getEndOfDay(this._date); | ||||
|         let allDay = (this._event.allDay || (this._event.date <= periodBegin && | ||||
|                                              this._event.end >= periodEnd)); | ||||
|         let allDay = this._event.allDay || (this._event.date <= periodBegin && | ||||
|                                              this._event.end >= periodEnd); | ||||
|         let title; | ||||
|         if (allDay) { | ||||
|             /* Translators: Shown in calendar event list for all day events | ||||
| @@ -702,12 +749,12 @@ var EventMessage = class EventMessage extends MessageList.Message { | ||||
|         } | ||||
|         return title; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var NotificationMessage = | ||||
| var NotificationMessage = GObject.registerClass( | ||||
| class NotificationMessage extends MessageList.Message { | ||||
|     constructor(notification) { | ||||
|         super(notification.title, notification.bannerBodyText); | ||||
|     _init(notification) { | ||||
|         super._init(notification.title, notification.bannerBodyText); | ||||
|         this.setUseBodyMarkup(notification.bannerBodyMarkup); | ||||
|  | ||||
|         this.notification = notification; | ||||
| @@ -730,11 +777,12 @@ class NotificationMessage extends MessageList.Message { | ||||
|     } | ||||
|  | ||||
|     _getIcon() { | ||||
|         if (this.notification.gicon) | ||||
|         if (this.notification.gicon) { | ||||
|             return new St.Icon({ gicon: this.notification.gicon, | ||||
|                                  icon_size: MESSAGE_ICON_SIZE }); | ||||
|         else | ||||
|         } else { | ||||
|             return this.notification.source.createIcon(MESSAGE_ICON_SIZE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _onUpdated(n, _clear) { | ||||
| @@ -744,7 +792,7 @@ class NotificationMessage extends MessageList.Message { | ||||
|         this.setUseBodyMarkup(n.bannerBodyMarkup); | ||||
|     } | ||||
|  | ||||
|     _onClicked() { | ||||
|     vfunc_clicked() { | ||||
|         this.notification.activate(); | ||||
|     } | ||||
|  | ||||
| @@ -766,11 +814,12 @@ class NotificationMessage extends MessageList.Message { | ||||
|     canClose() { | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var EventsSection = class EventsSection extends MessageList.MessageListSection { | ||||
|     constructor() { | ||||
|         super(); | ||||
| var EventsSection = GObject.registerClass( | ||||
| class EventsSection extends MessageList.MessageListSection { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|  | ||||
|         this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' }); | ||||
|         this._desktopSettings.connect('changed', this._reloadEvents.bind(this)); | ||||
| @@ -780,9 +829,9 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection { | ||||
|  | ||||
|         this._title = new St.Button({ style_class: 'events-section-title', | ||||
|                                       label: '', | ||||
|                                       x_align: St.Align.START, | ||||
|                                       can_focus: true }); | ||||
|         this.actor.insert_child_below(this._title, null); | ||||
|         this._title.child.x_align = Clutter.ActorAlign.START; | ||||
|         this.insert_child_below(this._title, null); | ||||
|  | ||||
|         this._title.connect('clicked', this._onTitleClicked.bind(this)); | ||||
|         this._title.connect('key-focus-in', this._onKeyFocusIn.bind(this)); | ||||
| @@ -793,6 +842,9 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection { | ||||
|     } | ||||
|  | ||||
|     setEventSource(eventSource) { | ||||
|         if (!(eventSource instanceof EventSourceBase)) | ||||
|             throw new Error('Event source is not valid type'); | ||||
|  | ||||
|         this._eventSource = eventSource; | ||||
|         this._eventSource.connect('changed', this._reloadEvents.bind(this)); | ||||
|     } | ||||
| @@ -809,14 +861,15 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection { | ||||
|  | ||||
|         let dayFormat; | ||||
|         let now = new Date(); | ||||
|         if (sameYear(this._date, now)) | ||||
|         if (sameYear(this._date, now)) { | ||||
|             /* Translators: Shown on calendar heading when selected day occurs on current year */ | ||||
|             dayFormat = Shell.util_translate_time_string(NC_("calendar heading", | ||||
|                                                              "%A, %B %-d")); | ||||
|         else | ||||
|         } else { | ||||
|             /* Translators: Shown on calendar heading when selected day occurs on different year */ | ||||
|             dayFormat = Shell.util_translate_time_string(NC_("calendar heading", | ||||
|                                                              "%A, %B %-d, %Y")); | ||||
|         } | ||||
|         this._title.label = this._date.toLocaleFormat(dayFormat); | ||||
|     } | ||||
|  | ||||
| @@ -857,7 +910,7 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection { | ||||
|  | ||||
|     _appInstalledChanged() { | ||||
|         this._calendarApp = undefined; | ||||
|         this._title.reactive = (this._getCalendarApp() != null); | ||||
|         this._title.reactive = this._getCalendarApp() != null; | ||||
|     } | ||||
|  | ||||
|     _getCalendarApp() { | ||||
| @@ -901,12 +954,29 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection { | ||||
|  | ||||
|         super._sync(); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var NotificationSection = | ||||
| var TimeLabel = GObject.registerClass( | ||||
| class NotificationTimeLabel extends St.Label { | ||||
|     _init(datetime) { | ||||
|         super._init({ | ||||
|             style_class: 'event-time', | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|             y_align: Clutter.ActorAlign.END, | ||||
|         }); | ||||
|         this._datetime = datetime; | ||||
|     } | ||||
|  | ||||
|     vfunc_map() { | ||||
|         this.text = Util.formatTimeSpan(this._datetime); | ||||
|         super.vfunc_map(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var NotificationSection = GObject.registerClass( | ||||
| class NotificationSection extends MessageList.MessageListSection { | ||||
|     constructor() { | ||||
|         super(); | ||||
|     _init() { | ||||
|         super._init(); | ||||
|  | ||||
|         this._sources = new Map(); | ||||
|         this._nUrgent = 0; | ||||
| @@ -915,8 +985,6 @@ class NotificationSection extends MessageList.MessageListSection { | ||||
|         Main.messageTray.getSources().forEach(source => { | ||||
|             this._sourceAdded(Main.messageTray, source); | ||||
|         }); | ||||
|  | ||||
|         this.actor.connect('notify::mapped', this._onMapped.bind(this)); | ||||
|     } | ||||
|  | ||||
|     get allowed() { | ||||
| @@ -924,24 +992,13 @@ class NotificationSection extends MessageList.MessageListSection { | ||||
|                !Main.sessionMode.isGreeter; | ||||
|     } | ||||
|  | ||||
|     _createTimeLabel(datetime) { | ||||
|         let label = new St.Label({ style_class: 'event-time', | ||||
|                                    x_align: Clutter.ActorAlign.START, | ||||
|                                    y_align: Clutter.ActorAlign.END }); | ||||
|         label.connect('notify::mapped', () => { | ||||
|             if (label.mapped) | ||||
|                 label.text = Util.formatTimeSpan(datetime); | ||||
|         }); | ||||
|         return label; | ||||
|     } | ||||
|  | ||||
|     _sourceAdded(tray, source) { | ||||
|         let obj = { | ||||
|             destroyId: 0, | ||||
|             notificationAddedId: 0, | ||||
|         }; | ||||
|  | ||||
|         obj.destroyId = source.connect('destroy', source => { | ||||
|         obj.destroyId = source.connect('destroy', () => { | ||||
|             this._onSourceDestroy(source, obj); | ||||
|         }); | ||||
|         obj.notificationAddedId = source.connect('notification-added', | ||||
| @@ -952,13 +1009,13 @@ class NotificationSection extends MessageList.MessageListSection { | ||||
|  | ||||
|     _onNotificationAdded(source, notification) { | ||||
|         let message = new NotificationMessage(notification); | ||||
|         message.setSecondaryActor(this._createTimeLabel(notification.datetime)); | ||||
|         message.setSecondaryActor(new TimeLabel(notification.datetime)); | ||||
|  | ||||
|         let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL; | ||||
|  | ||||
|         let updatedId = notification.connect('updated', () => { | ||||
|             message.setSecondaryActor(this._createTimeLabel(notification.datetime)); | ||||
|             this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.actor.mapped); | ||||
|             message.setSecondaryActor(new TimeLabel(notification.datetime)); | ||||
|             this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.mapped); | ||||
|         }); | ||||
|         let destroyId = notification.connect('destroy', () => { | ||||
|             notification.disconnect(destroyId); | ||||
| @@ -978,7 +1035,7 @@ class NotificationSection extends MessageList.MessageListSection { | ||||
|         } | ||||
|  | ||||
|         let index = isUrgent ? 0 : this._nUrgent; | ||||
|         this.addMessageAtIndex(message, index, this.actor.mapped); | ||||
|         this.addMessageAtIndex(message, index, this.mapped); | ||||
|     } | ||||
|  | ||||
|     _onSourceDestroy(source, obj) { | ||||
| @@ -988,25 +1045,23 @@ class NotificationSection extends MessageList.MessageListSection { | ||||
|         this._sources.delete(source); | ||||
|     } | ||||
|  | ||||
|     _onMapped() { | ||||
|         if (!this.actor.mapped) | ||||
|             return; | ||||
|  | ||||
|         for (let message of this._messages.keys()) | ||||
|     vfunc_map() { | ||||
|         this._messages.forEach(message => { | ||||
|             if (message.notification.urgency != MessageTray.Urgency.CRITICAL) | ||||
|                 message.notification.acknowledged = true; | ||||
|         }); | ||||
|         super.vfunc_map(); | ||||
|     } | ||||
|  | ||||
|     _shouldShow() { | ||||
|         return !this.empty && isToday(this._date); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var Placeholder = class Placeholder { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ style_class: 'message-list-placeholder', | ||||
|                                         vertical: true }); | ||||
| }); | ||||
|  | ||||
| var Placeholder = GObject.registerClass( | ||||
| class Placeholder extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ style_class: 'message-list-placeholder', vertical: true }); | ||||
|         this._date = new Date(); | ||||
|  | ||||
|         let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg'); | ||||
| @@ -1015,10 +1070,10 @@ var Placeholder = class Placeholder { | ||||
|         this._otherIcon = new Gio.FileIcon({ file: otherFile }); | ||||
|  | ||||
|         this._icon = new St.Icon(); | ||||
|         this.actor.add_actor(this._icon); | ||||
|         this.add_actor(this._icon); | ||||
|  | ||||
|         this._label = new St.Label(); | ||||
|         this.actor.add_actor(this._label); | ||||
|         this.add_actor(this._label); | ||||
|  | ||||
|         this._sync(); | ||||
|     } | ||||
| @@ -1045,48 +1100,56 @@ var Placeholder = class Placeholder { | ||||
|             this._label.text = _("No Events"); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var CalendarMessageList = class CalendarMessageList { | ||||
|     constructor() { | ||||
|         this.actor = new St.Widget({ style_class: 'message-list', | ||||
|                                      layout_manager: new Clutter.BinLayout(), | ||||
|                                      x_expand: true, y_expand: true }); | ||||
| var CalendarMessageList = GObject.registerClass( | ||||
| class CalendarMessageList extends St.Widget { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             style_class: 'message-list', | ||||
|             layout_manager: new Clutter.BinLayout(), | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|  | ||||
|         this._placeholder = new Placeholder(); | ||||
|         this.actor.add_actor(this._placeholder.actor); | ||||
|         this.add_actor(this._placeholder); | ||||
|  | ||||
|         let box = new St.BoxLayout({ vertical: true, | ||||
|                                      x_expand: true, y_expand: true }); | ||||
|         this.actor.add_actor(box); | ||||
|         this.add_actor(box); | ||||
|  | ||||
|         this._scrollView = new St.ScrollView({ style_class: 'vfade', | ||||
|                                                overlay_scrollbars: true, | ||||
|                                                x_expand: true, y_expand: true, | ||||
|                                                x_fill: true, y_fill: true }); | ||||
|         this._scrollView = new St.ScrollView({ | ||||
|             style_class: 'vfade', | ||||
|             overlay_scrollbars: true, | ||||
|             x_expand: true, y_expand: true, | ||||
|         }); | ||||
|         this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); | ||||
|         box.add_actor(this._scrollView); | ||||
|  | ||||
|         this._clearButton = new St.Button({ style_class: 'message-list-clear-button button', | ||||
|                                             label: _("Clear"), | ||||
|                                             can_focus: true }); | ||||
|         this._clearButton.set_x_align(Clutter.ActorAlign.END); | ||||
|         this._clearButton = new St.Button({ | ||||
|             style_class: 'message-list-clear-button button', | ||||
|             label: _('Clear'), | ||||
|             can_focus: true, | ||||
|             x_align: Clutter.ActorAlign.END, | ||||
|         }); | ||||
|         this._clearButton.connect('clicked', () => { | ||||
|             let sections = [...this._sections.keys()]; | ||||
|             sections.forEach(s => s.clear()); | ||||
|             this._sectionList.get_children().forEach(s => s.clear()); | ||||
|         }); | ||||
|         box.add_actor(this._clearButton); | ||||
|  | ||||
|         this._placeholder.actor.bind_property('visible', | ||||
|         this._placeholder.bind_property('visible', | ||||
|             this._clearButton, 'visible', | ||||
|             GObject.BindingFlags.INVERT_BOOLEAN); | ||||
|  | ||||
|         this._sectionList = new St.BoxLayout({ style_class: 'message-list-sections', | ||||
|                                                vertical: true, | ||||
|                                                x_expand: true, | ||||
|                                                y_expand: true, | ||||
|                                                y_align: Clutter.ActorAlign.START }); | ||||
|         this._sectionList.connect('actor-added', this._sync.bind(this)); | ||||
|         this._sectionList.connect('actor-removed', this._sync.bind(this)); | ||||
|         this._scrollView.add_actor(this._sectionList); | ||||
|         this._sections = new Map(); | ||||
|  | ||||
|         this._mediaSection = new Mpris.MediaSection(); | ||||
|         this._addSection(this._mediaSection); | ||||
| @@ -1101,58 +1164,35 @@ var CalendarMessageList = class CalendarMessageList { | ||||
|     } | ||||
|  | ||||
|     _addSection(section) { | ||||
|         let obj = { | ||||
|             destroyId: 0, | ||||
|             visibleId: 0, | ||||
|             emptyChangedId: 0, | ||||
|             canClearChangedId: 0, | ||||
|             keyFocusId: 0 | ||||
|         }; | ||||
|         obj.destroyId = section.actor.connect('destroy', () => { | ||||
|             this._removeSection(section); | ||||
|         }); | ||||
|         obj.visibleId = section.actor.connect('notify::visible', | ||||
|                                               this._sync.bind(this)); | ||||
|         obj.emptyChangedId = section.connect('empty-changed', | ||||
|                                              this._sync.bind(this)); | ||||
|         obj.canClearChangedId = section.connect('can-clear-changed', | ||||
|                                                 this._sync.bind(this)); | ||||
|         obj.keyFocusId = section.connect('key-focus-in', | ||||
|                                          this._onKeyFocusIn.bind(this)); | ||||
|         let connectionsIds = []; | ||||
|  | ||||
|         this._sections.set(section, obj); | ||||
|         this._sectionList.add_actor(section.actor); | ||||
|         this._sync(); | ||||
|     } | ||||
|         for (let prop of ['visible', 'empty', 'can-clear']) { | ||||
|             connectionsIds.push( | ||||
|                 section.connect(`notify::${prop}`, this._sync.bind(this))); | ||||
|         } | ||||
|         connectionsIds.push(section.connect('message-focused', (_s, messageActor) => { | ||||
|             Util.ensureActorVisibleInScrollView(this._scrollView, messageActor); | ||||
|         })); | ||||
|  | ||||
|     _removeSection(section) { | ||||
|         let obj = this._sections.get(section); | ||||
|         section.actor.disconnect(obj.destroyId); | ||||
|         section.actor.disconnect(obj.visibleId); | ||||
|         section.disconnect(obj.emptyChangedId); | ||||
|         section.disconnect(obj.canClearChangedId); | ||||
|         section.disconnect(obj.keyFocusId); | ||||
|         connectionsIds.push(section.connect('destroy', () => { | ||||
|             connectionsIds.forEach(id => section.disconnect(id)); | ||||
|             this._sectionList.remove_actor(section); | ||||
|         })); | ||||
|  | ||||
|         this._sections.delete(section); | ||||
|         this._sectionList.remove_actor(section.actor); | ||||
|         this._sync(); | ||||
|     } | ||||
|  | ||||
|     _onKeyFocusIn(section, actor) { | ||||
|         Util.ensureActorVisibleInScrollView(this._scrollView, actor); | ||||
|         this._sectionList.add_actor(section); | ||||
|     } | ||||
|  | ||||
|     _sync() { | ||||
|         let sections = [...this._sections.keys()]; | ||||
|         let sections = this._sectionList.get_children(); | ||||
|         let visible = sections.some(s => s.allowed); | ||||
|         this.actor.visible = visible; | ||||
|         this.visible = visible; | ||||
|         if (!visible) | ||||
|             return; | ||||
|  | ||||
|         let empty = sections.every(s => s.empty || !s.actor.visible); | ||||
|         this._placeholder.actor.visible = empty; | ||||
|         let empty = sections.every(s => s.empty || !s.visible); | ||||
|         this._placeholder.visible = empty; | ||||
|  | ||||
|         let canClear = sections.some(s => s.canClear && s.actor.visible); | ||||
|         let canClear = sections.some(s => s.canClear && s.visible); | ||||
|         this._clearButton.reactive = canClear; | ||||
|     } | ||||
|  | ||||
| @@ -1161,8 +1201,7 @@ var CalendarMessageList = class CalendarMessageList { | ||||
|     } | ||||
|  | ||||
|     setDate(date) { | ||||
|         for (let section of this._sections.keys()) | ||||
|             section.setDate(date); | ||||
|         this._sectionList.get_children().forEach(s => s.setDate(date)); | ||||
|         this._placeholder.setDate(date); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|   | ||||
| @@ -1,19 +1,22 @@ | ||||
| /* exported CheckBox */ | ||||
| const { Clutter, Pango, St } = imports.gi; | ||||
| const { Clutter, GObject, Pango, St } = imports.gi; | ||||
|  | ||||
| var CheckBox = class CheckBox { | ||||
|     constructor(label) { | ||||
|         let container = new St.BoxLayout(); | ||||
|         this.actor = new St.Button({ style_class: 'check-box', | ||||
|                                      child: container, | ||||
|                                      button_mask: St.ButtonMask.ONE, | ||||
|                                      toggle_mode: true, | ||||
|                                      can_focus: true, | ||||
|                                      x_fill: true, | ||||
|                                      y_fill: true }); | ||||
| var CheckBox = GObject.registerClass( | ||||
| class CheckBox extends St.Button { | ||||
|     _init(label) { | ||||
|         let container = new St.BoxLayout({ | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         super._init({ | ||||
|             style_class: 'check-box', | ||||
|             child: container, | ||||
|             button_mask: St.ButtonMask.ONE, | ||||
|             toggle_mode: true, | ||||
|             can_focus: true, | ||||
|         }); | ||||
|  | ||||
|         this._box = new St.Bin(); | ||||
|         this._box.set_y_align(Clutter.ActorAlign.START); | ||||
|         this._box = new St.Bin({ y_align: Clutter.ActorAlign.START }); | ||||
|         container.add_actor(this._box); | ||||
|  | ||||
|         this._label = new St.Label(); | ||||
| @@ -32,4 +35,4 @@ var CheckBox = class CheckBox { | ||||
|     getLabelActor() { | ||||
|         return this._label; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported CloseDialog */ | ||||
|  | ||||
| const { Clutter, Gio, GLib, GObject, Meta, Shell } = imports.gi; | ||||
| const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; | ||||
|  | ||||
| const Dialog = imports.ui.dialog; | ||||
| const Main = imports.ui.main; | ||||
| @@ -13,7 +13,7 @@ var ALIVE_TIMEOUT = 5000; | ||||
| var CloseDialog = GObject.registerClass({ | ||||
|     Implements: [Meta.CloseDialog], | ||||
|     Properties: { | ||||
|         'window': GObject.ParamSpec.override('window', Meta.CloseDialog) | ||||
|         'window': GObject.ParamSpec.override('window', Meta.CloseDialog), | ||||
|     }, | ||||
| }, class CloseDialog extends GObject.Object { | ||||
|     _init(window) { | ||||
| @@ -46,6 +46,18 @@ var CloseDialog = GObject.registerClass({ | ||||
|         return new Dialog.MessageDialogContent({ icon, title, subtitle }); | ||||
|     } | ||||
|  | ||||
|     _updateScale() { | ||||
|         // Since this is a child of MetaWindowActor (which, for Wayland clients, | ||||
|         // applies the geometry scale factor to its children itself, see | ||||
|         // meta_window_actor_set_geometry_scale()), make sure we don't apply | ||||
|         // the factor twice in the end. | ||||
|         if (this._window.get_client_type() !== Meta.WindowClientType.WAYLAND) | ||||
|             return; | ||||
|  | ||||
|         let { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); | ||||
|         this._dialog.set_scale(1 / scaleFactor, 1 / scaleFactor); | ||||
|     } | ||||
|  | ||||
|     _initDialog() { | ||||
|         if (this._dialog) | ||||
|             return; | ||||
| @@ -61,9 +73,14 @@ var CloseDialog = GObject.registerClass({ | ||||
|                                  default: true }); | ||||
|         this._dialog.addButton({ label: _('Wait'), | ||||
|                                  action: this._onWait.bind(this), | ||||
|                                  key: Clutter.Escape }); | ||||
|                                  key: Clutter.KEY_Escape }); | ||||
|  | ||||
|         global.focus_manager.add_group(this._dialog); | ||||
|  | ||||
|         let themeContext = St.ThemeContext.get_for_stage(global.stage); | ||||
|         themeContext.connect('notify::scale-factor', this._updateScale.bind(this)); | ||||
|  | ||||
|         this._updateScale(); | ||||
|     } | ||||
|  | ||||
|     _addWindowEffect() { | ||||
| @@ -107,11 +124,12 @@ var CloseDialog = GObject.registerClass({ | ||||
|         if (this._tracked === shouldTrack) | ||||
|             return; | ||||
|  | ||||
|         if (shouldTrack) | ||||
|         if (shouldTrack) { | ||||
|             Main.layoutManager.trackChrome(this._dialog, | ||||
|                                            { affectsInputRegion: true }); | ||||
|         else | ||||
|         } else { | ||||
|             Main.layoutManager.untrackChrome(this._dialog); | ||||
|         } | ||||
|  | ||||
|         // The buttons are broken when they aren't added to the input region, | ||||
|         // so disable them properly in that case | ||||
| @@ -145,14 +163,14 @@ var CloseDialog = GObject.registerClass({ | ||||
|         this._addWindowEffect(); | ||||
|         this._initDialog(); | ||||
|  | ||||
|         this._dialog.scale_y = 0; | ||||
|         this._dialog.set_pivot_point(0.5, 0.5); | ||||
|         this._dialog._dialog.scale_y = 0; | ||||
|         this._dialog._dialog.set_pivot_point(0.5, 0.5); | ||||
|  | ||||
|         this._dialog.ease({ | ||||
|         this._dialog._dialog.ease({ | ||||
|             scale_y: 1, | ||||
|             mode: Clutter.AnimationMode.LINEAR, | ||||
|             duration: DIALOG_TRANSITION_TIME, | ||||
|             onComplete: this._onFocusChanged.bind(this) | ||||
|             onComplete: this._onFocusChanged.bind(this), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -175,11 +193,11 @@ var CloseDialog = GObject.registerClass({ | ||||
|         this._dialog = null; | ||||
|         this._removeWindowEffect(); | ||||
|  | ||||
|         dialog.ease({ | ||||
|         dialog._dialog.ease({ | ||||
|             scale_y: 0, | ||||
|             mode: Clutter.AnimationMode.LINEAR, | ||||
|             duration: DIALOG_TRANSITION_TIME, | ||||
|             onComplete: () => dialog.destroy() | ||||
|             onComplete: () => dialog.destroy(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -58,9 +58,8 @@ var AutomountManager = class { | ||||
|     _InhibitorsChanged(_object, _senderName, [_inhibitor]) { | ||||
|         this._session.IsInhibitedRemote(GNOME_SESSION_AUTOMOUNT_INHIBIT, | ||||
|             (result, error) => { | ||||
|                 if (!error) { | ||||
|                 if (!error) | ||||
|                     this._inhibited = result[0]; | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
| @@ -110,7 +109,7 @@ var AutomountManager = class { | ||||
|         // mount operation object | ||||
|         if (drive.can_stop()) { | ||||
|             drive.stop(Gio.MountUnmountFlags.FORCE, null, null, | ||||
|                 (drive, res) => { | ||||
|                 (o, res) => { | ||||
|                     try { | ||||
|                         drive.stop_finish(res); | ||||
|                     } catch (e) { | ||||
| @@ -119,7 +118,7 @@ var AutomountManager = class { | ||||
|                 }); | ||||
|         } else if (drive.can_eject()) { | ||||
|             drive.eject_with_operation(Gio.MountUnmountFlags.FORCE, null, null, | ||||
|                 (drive, res) => { | ||||
|                 (o, res) => { | ||||
|                     try { | ||||
|                         drive.eject_with_operation_finish(res); | ||||
|                     } catch (e) { | ||||
| @@ -223,7 +222,7 @@ var AutomountManager = class { | ||||
|             delete volume._allowAutorunExpireId; | ||||
|         } | ||||
|         this._volumeQueue = | ||||
|             this._volumeQueue.filter(element => (element != volume)); | ||||
|             this._volumeQueue.filter(element => element != volume); | ||||
|     } | ||||
|  | ||||
|     _reaskPassword(volume) { | ||||
| @@ -231,7 +230,7 @@ var AutomountManager = class { | ||||
|         let existingDialog = prevOperation ? prevOperation.borrowDialog() : null; | ||||
|         let operation = | ||||
|             new ShellMountOperation.ShellMountOperation(volume, | ||||
|                                                         { existingDialog: existingDialog }); | ||||
|                                                         { existingDialog }); | ||||
|         this._mountVolume(volume, operation); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported Component */ | ||||
|  | ||||
| const { Gio, St } = imports.gi; | ||||
| const { Clutter, Gio, GObject, St } = imports.gi; | ||||
|  | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const Main = imports.ui.main; | ||||
| @@ -20,7 +20,7 @@ var AutorunSetting = { | ||||
|     RUN: 0, | ||||
|     IGNORE: 1, | ||||
|     FILES: 2, | ||||
|     ASK: 3 | ||||
|     ASK: 3, | ||||
| }; | ||||
|  | ||||
| // misc utils | ||||
| @@ -41,7 +41,7 @@ function isMountRootHidden(root) { | ||||
|     let path = root.get_path(); | ||||
|  | ||||
|     // skip any mounts in hidden directory hierarchies | ||||
|     return (path.includes('/.')); | ||||
|     return path.includes('/.'); | ||||
| } | ||||
|  | ||||
| function isMountNonLocal(mount) { | ||||
| @@ -52,7 +52,7 @@ function isMountNonLocal(mount) { | ||||
|     if (volume == null) | ||||
|         return true; | ||||
|  | ||||
|     return (volume.get_identifier("class") == "network"); | ||||
|     return volume.get_identifier("class") == "network"; | ||||
| } | ||||
|  | ||||
| function startAppForMount(app, mount) { | ||||
| @@ -115,7 +115,8 @@ var ContentTypeDiscoverer = class { | ||||
|  | ||||
|             let hotplugSniffer = new HotplugSniffer(); | ||||
|             hotplugSniffer.SniffURIRemote(root.get_uri(), | ||||
|                 ([contentTypes]) => { | ||||
|                 result => { | ||||
|                     [contentTypes] = result; | ||||
|                     this._emitCallback(mount, contentTypes); | ||||
|                 }); | ||||
|         } | ||||
| @@ -124,7 +125,7 @@ var ContentTypeDiscoverer = class { | ||||
|     _emitCallback(mount, contentTypes = []) { | ||||
|         // we're not interested in win32 software content types here | ||||
|         contentTypes = contentTypes.filter( | ||||
|             type => (type != 'x-content/win32-software') | ||||
|             type => type != 'x-content/win32-software' | ||||
|         ); | ||||
|  | ||||
|         let apps = []; | ||||
| @@ -166,7 +167,7 @@ var AutorunManager = class { | ||||
|         if (!this._session.SessionIsActive) | ||||
|             return; | ||||
|  | ||||
|         let discoverer = new ContentTypeDiscoverer((mount, apps, contentTypes) => { | ||||
|         let discoverer = new ContentTypeDiscoverer((m, apps, contentTypes) => { | ||||
|             this._dispatcher.addMount(mount, apps, contentTypes); | ||||
|         }); | ||||
|         discoverer.guessContentTypes(mount); | ||||
| @@ -201,7 +202,7 @@ var AutorunDispatcher = class { | ||||
|     } | ||||
|  | ||||
|     _getSourceForMount(mount) { | ||||
|         let filtered = this._sources.filter(source => (source.mount == mount)); | ||||
|         let filtered = this._sources.filter(source => source.mount == mount); | ||||
|  | ||||
|         // we always make sure not to add two sources for the same | ||||
|         // mount in addMount(), so it's safe to assume filtered.length | ||||
| @@ -245,11 +246,10 @@ var AutorunDispatcher = class { | ||||
|         let success = false; | ||||
|         let app = null; | ||||
|  | ||||
|         if (setting == AutorunSetting.RUN) { | ||||
|         if (setting == AutorunSetting.RUN) | ||||
|             app = Gio.app_info_get_default_for_type(contentTypes[0], false); | ||||
|         } else if (setting == AutorunSetting.FILES) { | ||||
|         else if (setting == AutorunSetting.FILES) | ||||
|             app = Gio.app_info_get_default_for_type('inode/directory', false); | ||||
|         } | ||||
|  | ||||
|         if (app) | ||||
|             success = startAppForMount(app, mount); | ||||
| @@ -272,9 +272,10 @@ var AutorunDispatcher = class { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var AutorunSource = class extends MessageTray.Source { | ||||
|     constructor(manager, mount, apps) { | ||||
|         super(mount.get_name()); | ||||
| var AutorunSource = GObject.registerClass( | ||||
| class AutorunSource extends MessageTray.Source { | ||||
|     _init(manager, mount, apps) { | ||||
|         super._init(mount.get_name()); | ||||
|  | ||||
|         this._manager = manager; | ||||
|         this.mount = mount; | ||||
| @@ -284,7 +285,7 @@ var AutorunSource = class extends MessageTray.Source { | ||||
|  | ||||
|         // add ourselves as a source, and popup the notification | ||||
|         Main.messageTray.add(this); | ||||
|         this.notify(this._notification); | ||||
|         this.showNotification(this._notification); | ||||
|     } | ||||
|  | ||||
|     getIcon() { | ||||
| @@ -294,11 +295,12 @@ var AutorunSource = class extends MessageTray.Source { | ||||
|     _createPolicy() { | ||||
|         return new MessageTray.NotificationApplicationPolicy('org.gnome.Nautilus'); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var AutorunNotification = class extends MessageTray.Notification { | ||||
|     constructor(manager, source) { | ||||
|         super(source, source.title); | ||||
| var AutorunNotification = GObject.registerClass( | ||||
| class AutorunNotification extends MessageTray.Notification { | ||||
|     _init(manager, source) { | ||||
|         super._init(source, source.title); | ||||
|  | ||||
|         this._manager = manager; | ||||
|         this._mount = source.mount; | ||||
| @@ -318,20 +320,23 @@ var AutorunNotification = class extends MessageTray.Notification { | ||||
|     } | ||||
|  | ||||
|     _buttonForApp(app) { | ||||
|         let box = new St.BoxLayout(); | ||||
|         let box = new St.BoxLayout({ | ||||
|             x_expand: true, | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|         let icon = new St.Icon({ gicon: app.get_icon(), | ||||
|                                  style_class: 'hotplug-notification-item-icon' }); | ||||
|         box.add(icon); | ||||
|  | ||||
|         let label = new St.Bin({ | ||||
|             y_align: St.Align.MIDDLE, | ||||
|             child: new St.Label({ text: _("Open with %s").format(app.get_name()) }), | ||||
|             child: new St.Label({ | ||||
|                 text: _("Open with %s").format(app.get_name()), | ||||
|                 y_align: Clutter.ActorAlign.CENTER, | ||||
|             }), | ||||
|         }); | ||||
|         box.add(label); | ||||
|  | ||||
|         let button = new St.Button({ child: box, | ||||
|                                      x_fill: true, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      x_expand: true, | ||||
|                                      button_mask: St.ButtonMask.ONE, | ||||
|                                      style_class: 'hotplug-notification-item button' }); | ||||
| @@ -350,6 +355,6 @@ var AutorunNotification = class extends MessageTray.Notification { | ||||
|         let app = Gio.app_info_get_default_for_type('inode/directory', false); | ||||
|         startAppForMount(app, this._mount); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var Component = AutorunManager; | ||||
|   | ||||
| @@ -33,7 +33,7 @@ class KeyringDialog extends ModalDialog.ModalDialog { | ||||
|  | ||||
|         this._cancelButton = this.addButton({ label: '', | ||||
|                                               action: this._onCancelButton.bind(this), | ||||
|                                               key: Clutter.Escape }); | ||||
|                                               key: Clutter.KEY_Escape }); | ||||
|         this._continueButton = this.addButton({ label: '', | ||||
|                                                 action: this._onContinueButton.bind(this), | ||||
|                                                 default: true }); | ||||
| @@ -54,8 +54,12 @@ class KeyringDialog extends ModalDialog.ModalDialog { | ||||
|  | ||||
|     _buildControlTable() { | ||||
|         let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         let table = new St.Widget({ style_class: 'keyring-dialog-control-table', | ||||
|                                     layout_manager: layout }); | ||||
|         let table = new St.Widget({ | ||||
|             style_class: 'keyring-dialog-control-table', | ||||
|             layout_manager: layout, | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         layout.hookup_style(table); | ||||
|         let rtl = table.get_text_direction() == Clutter.TextDirection.RTL; | ||||
|         let row = 0; | ||||
| @@ -74,16 +78,18 @@ class KeyringDialog extends ModalDialog.ModalDialog { | ||||
|             ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); | ||||
|             this._passwordEntry.clutter_text.connect('activate', this._onPasswordActivate.bind(this)); | ||||
|  | ||||
|             this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, true); | ||||
|             this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, { | ||||
|                 animate: true, | ||||
|             }); | ||||
|  | ||||
|             if (rtl) { | ||||
|                 layout.attach(this._workSpinner.actor, 0, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner, 0, row, 1, 1); | ||||
|                 layout.attach(this._passwordEntry, 1, row, 1, 1); | ||||
|                 layout.attach(label, 2, row, 1, 1); | ||||
|             } else { | ||||
|                 layout.attach(label, 0, row, 1, 1); | ||||
|                 layout.attach(this._passwordEntry, 1, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner.actor, 2, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner, 2, row, 1, 1); | ||||
|             } | ||||
|             row++; | ||||
|         } else { | ||||
| @@ -92,9 +98,9 @@ class KeyringDialog extends ModalDialog.ModalDialog { | ||||
|         } | ||||
|  | ||||
|         if (this.prompt.confirm_visible) { | ||||
|             var label = new St.Label(({ style_class: 'prompt-dialog-password-label', | ||||
|                                         x_align: Clutter.ActorAlign.START, | ||||
|                                         y_align: Clutter.ActorAlign.CENTER })); | ||||
|             var label = new St.Label({ style_class: 'prompt-dialog-password-label', | ||||
|                                        x_align: Clutter.ActorAlign.START, | ||||
|                                        y_align: Clutter.ActorAlign.CENTER }); | ||||
|             label.set_text(_("Type again:")); | ||||
|             this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', | ||||
|                                                 text: '', | ||||
| @@ -121,8 +127,8 @@ class KeyringDialog extends ModalDialog.ModalDialog { | ||||
|         if (this.prompt.choice_visible) { | ||||
|             let choice = new CheckBox.CheckBox(); | ||||
|             this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE); | ||||
|             this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL); | ||||
|             layout.attach(choice.actor, rtl ? 0 : 1, row, 1, 1); | ||||
|             this.prompt.bind_property('choice-chosen', choice, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL); | ||||
|             layout.attach(choice, rtl ? 0 : 1, row, 1, 1); | ||||
|             row++; | ||||
|         } | ||||
|  | ||||
| @@ -140,7 +146,7 @@ class KeyringDialog extends ModalDialog.ModalDialog { | ||||
|         } | ||||
|  | ||||
|         this._controlTable = table; | ||||
|         this._content.messageBox.add(table, { x_fill: true, y_fill: true }); | ||||
|         this._content.messageBox.add_child(table); | ||||
|     } | ||||
|  | ||||
|     _updateSensitivity(sensitive) { | ||||
| @@ -228,10 +234,11 @@ var KeyringDummyDialog = class { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var KeyringPrompter = class { | ||||
|     constructor() { | ||||
|         this._prompter = new Gcr.SystemPrompter(); | ||||
|         this._prompter.connect('new-prompt', () => { | ||||
| var KeyringPrompter = GObject.registerClass( | ||||
| class KeyringPrompter extends Gcr.SystemPrompter { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|         this.connect('new-prompt', () => { | ||||
|             let dialog = this._enabled | ||||
|                 ? new KeyringDialog() | ||||
|                 : new KeyringDummyDialog(); | ||||
| @@ -246,7 +253,7 @@ var KeyringPrompter = class { | ||||
|  | ||||
|     enable() { | ||||
|         if (!this._registered) { | ||||
|             this._prompter.register(Gio.DBus.session); | ||||
|             this.register(Gio.DBus.session); | ||||
|             this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter', | ||||
|                                                      Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null); | ||||
|             this._registered = true; | ||||
| @@ -257,10 +264,10 @@ var KeyringPrompter = class { | ||||
|     disable() { | ||||
|         this._enabled = false; | ||||
|  | ||||
|         if (this._prompter.prompting) | ||||
|         if (this.prompting) | ||||
|             this._currentPrompt.cancel(); | ||||
|         this._currentPrompt = null; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var Component = KeyringPrompter; | ||||
|   | ||||
| @@ -56,7 +56,7 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog { | ||||
|  | ||||
|             secret.entry = new St.Entry({ style_class: 'prompt-dialog-password-entry', | ||||
|                                           text: secret.value, can_focus: reactive, | ||||
|                                           reactive: reactive, | ||||
|                                           reactive, | ||||
|                                           x_expand: true }); | ||||
|             ShellEntry.addContextMenu(secret.entry, | ||||
|                                       { isPassword: secret.password }); | ||||
| @@ -106,10 +106,7 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog { | ||||
|             descriptionLabel.clutter_text.line_wrap = true; | ||||
|             descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|  | ||||
|             contentBox.messageBox.add(descriptionLabel, | ||||
|                                       { y_fill: true, | ||||
|                                         y_align: St.Align.START, | ||||
|                                         expand: true }); | ||||
|             contentBox.messageBox.add_child(descriptionLabel); | ||||
|         } | ||||
|  | ||||
|         this._okButton = { | ||||
| @@ -172,7 +169,7 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return (value.length >= 8 && value.length <= 63); | ||||
|         return value.length >= 8 && value.length <= 63; | ||||
|     } | ||||
|  | ||||
|     _validateStaticWep(secret) { | ||||
| @@ -225,11 +222,12 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog { | ||||
|                            validate: this._validateStaticWep, password: true }); | ||||
|             break; | ||||
|         case 'ieee8021x': | ||||
|             if (wirelessSecuritySetting.auth_alg == 'leap') // Cisco LEAP | ||||
|             if (wirelessSecuritySetting.auth_alg == 'leap') { // Cisco LEAP | ||||
|                 secrets.push({ label: _("Password: "), key: 'leap-password', | ||||
|                                value: wirelessSecuritySetting.leap_password || '', password: true }); | ||||
|             else // Dynamic (IEEE 802.1x) WEP | ||||
|             } else { // Dynamic (IEEE 802.1x) WEP | ||||
|                 this._get8021xSecrets(secrets); | ||||
|             } | ||||
|             break; | ||||
|         case 'wpa-eap': | ||||
|             this._get8021xSecrets(secrets); | ||||
| @@ -244,15 +242,18 @@ class NetworkSecretDialog extends ModalDialog.ModalDialog { | ||||
|  | ||||
|         /* If hints were given we know exactly what we need to ask */ | ||||
|         if (this._settingName == "802-1x" && this._hints.length) { | ||||
|             if (this._hints.includes('identity')) | ||||
|             if (this._hints.includes('identity')) { | ||||
|                 secrets.push({ label: _("Username: "), key: 'identity', | ||||
|                                value: ieee8021xSetting.identity || '', password: false }); | ||||
|             if (this._hints.includes('password')) | ||||
|             } | ||||
|             if (this._hints.includes('password')) { | ||||
|                 secrets.push({ label: _("Password: "), key: 'password', | ||||
|                                value: ieee8021xSetting.password || '', password: true }); | ||||
|             if (this._hints.includes('private-key-password')) | ||||
|             } | ||||
|             if (this._hints.includes('private-key-password')) { | ||||
|                 secrets.push({ label: _("Private key password: "), key: 'private-key-password', | ||||
|                                value: ieee8021xSetting.private_key_password || '', password: true }); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -556,7 +557,7 @@ var VPNRequestHandler = class { | ||||
|                     contentOverride.secrets.push({ | ||||
|                         label: keyfile.get_string(groups[i], 'Label'), | ||||
|                         key: groups[i], | ||||
|                         value: value, | ||||
|                         value, | ||||
|                         password: keyfile.get_boolean(groups[i], 'IsSecret'), | ||||
|                     }); | ||||
|                 } else { | ||||
| @@ -734,7 +735,7 @@ var NetworkAgent = class { | ||||
|         }); | ||||
|  | ||||
|         Main.messageTray.add(source); | ||||
|         source.notify(notification); | ||||
|         source.showNotification(notification); | ||||
|     } | ||||
|  | ||||
|     _newRequest(agent, requestId, connection, settingName, hints, flags) { | ||||
|   | ||||
| @@ -11,12 +11,19 @@ const ModalDialog = imports.ui.modalDialog; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const UserWidget = imports.ui.userWidget; | ||||
|  | ||||
| const DialogMode = { | ||||
|     AUTH: 0, | ||||
|     CONFIRM: 1, | ||||
| }; | ||||
|  | ||||
| var DIALOG_ICON_SIZE = 48; | ||||
|  | ||||
| var WORK_SPINNER_ICON_SIZE = 16; | ||||
|  | ||||
| const DELAYED_RESET_TIMEOUT = 200; | ||||
|  | ||||
| var AuthenticationDialog = GObject.registerClass({ | ||||
|     Signals: { 'done': { param_types: [GObject.TYPE_BOOLEAN] } } | ||||
|     Signals: { 'done': { param_types: [GObject.TYPE_BOOLEAN] } }, | ||||
| }, class AuthenticationDialog extends ModalDialog.ModalDialog { | ||||
|     _init(actionId, body, cookie, userNames) { | ||||
|         super._init({ styleClass: 'prompt-dialog' }); | ||||
| @@ -24,7 +31,6 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|         this.actionId = actionId; | ||||
|         this.message = body; | ||||
|         this.userNames = userNames; | ||||
|         this._wasDismissed = false; | ||||
|  | ||||
|         this._sessionUpdatedId = Main.sessionMode.connect('updated', () => { | ||||
|             this.visible = !Main.sessionMode.isLocked; | ||||
| @@ -51,70 +57,63 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|             userName = userNames[0]; | ||||
|  | ||||
|         this._user = AccountsService.UserManager.get_default().get_user(userName); | ||||
|         let userRealName = this._user.get_real_name(); | ||||
|         this._userLoadedId = this._user.connect('notify::is_loaded', | ||||
|                                                 this._onUserChanged.bind(this)); | ||||
|         this._userChangedId = this._user.connect('changed', | ||||
|                                                  this._onUserChanged.bind(this)); | ||||
|  | ||||
|         // Special case 'root' | ||||
|         let userIsRoot = false; | ||||
|         if (userName == 'root') { | ||||
|             userIsRoot = true; | ||||
|             userRealName = _("Administrator"); | ||||
|         } | ||||
|         let userBox = new St.BoxLayout({ | ||||
|             style_class: 'polkit-dialog-user-layout', | ||||
|             vertical: false, | ||||
|         }); | ||||
|         content.messageBox.add(userBox); | ||||
|  | ||||
|         if (userIsRoot) { | ||||
|             let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label', | ||||
|                                             text: userRealName })); | ||||
|             content.messageBox.add(userLabel, { x_fill: false, | ||||
|                                                 x_align: St.Align.START }); | ||||
|         } else { | ||||
|             let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout', | ||||
|                                              vertical: false }); | ||||
|             content.messageBox.add(userBox); | ||||
|             this._userAvatar = new UserWidget.Avatar(this._user, | ||||
|                                                      { iconSize: DIALOG_ICON_SIZE, | ||||
|                                                        styleClass: 'polkit-dialog-user-icon' }); | ||||
|             this._userAvatar.actor.hide(); | ||||
|             userBox.add(this._userAvatar.actor, | ||||
|                         { x_fill: true, | ||||
|                           y_fill: false, | ||||
|                           x_align: St.Align.END, | ||||
|                           y_align: St.Align.START }); | ||||
|             let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-label', | ||||
|                                             text: userRealName })); | ||||
|             userBox.add(userLabel, | ||||
|                         { x_fill: true, | ||||
|                           y_fill: false, | ||||
|                           x_align: St.Align.END, | ||||
|                           y_align: St.Align.MIDDLE }); | ||||
|         } | ||||
|         this._userAvatar = new UserWidget.Avatar(this._user, { | ||||
|             iconSize: DIALOG_ICON_SIZE, | ||||
|             styleClass: 'polkit-dialog-user-icon', | ||||
|         }); | ||||
|         userBox.add_child(this._userAvatar); | ||||
|  | ||||
|         this._onUserChanged(); | ||||
|         this._userLabel = new St.Label({ | ||||
|             style_class: userName === 'root' | ||||
|                 ? 'polkit-dialog-user-root-label' | ||||
|                 : 'polkit-dialog-user-label', | ||||
|             x_expand: true, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|  | ||||
|         if (userName === 'root') | ||||
|             this._userLabel.text = _('Administrator'); | ||||
|  | ||||
|         userBox.add_child(this._userLabel); | ||||
|  | ||||
|         this._passwordBox = new St.BoxLayout({ vertical: false, style_class: 'prompt-dialog-password-box' }); | ||||
|         content.messageBox.add(this._passwordBox); | ||||
|         this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label' })); | ||||
|         this._passwordBox.add(this._passwordLabel, { y_fill: false, y_align: St.Align.MIDDLE }); | ||||
|         this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', | ||||
|                                              text: "", | ||||
|                                              can_focus: true }); | ||||
|         this._passwordLabel = new St.Label({ | ||||
|             style_class: 'prompt-dialog-password-label', | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         this._passwordBox.add_child(this._passwordLabel); | ||||
|         this._passwordEntry = new St.Entry({ | ||||
|             style_class: 'prompt-dialog-password-entry', | ||||
|             text: "", | ||||
|             can_focus: true, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); | ||||
|         this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this)); | ||||
|         this._passwordBox.add(this._passwordEntry, | ||||
|                               { expand: true }); | ||||
|         this._passwordEntry.bind_property('reactive', | ||||
|             this._passwordEntry.clutter_text, 'editable', | ||||
|             GObject.BindingFlags.SYNC_CREATE); | ||||
|         this._passwordBox.add_child(this._passwordEntry); | ||||
|  | ||||
|         this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, true); | ||||
|         this._passwordBox.add(this._workSpinner.actor); | ||||
|         this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, { | ||||
|             animate: true, | ||||
|         }); | ||||
|         this._passwordBox.add(this._workSpinner); | ||||
|  | ||||
|         this.setInitialKeyFocus(this._passwordEntry); | ||||
|         this._passwordBox.hide(); | ||||
|  | ||||
|         this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' }); | ||||
|         this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._errorMessageLabel.clutter_text.line_wrap = true; | ||||
|         content.messageBox.add(this._errorMessageLabel, { x_fill: false, x_align: St.Align.START }); | ||||
|         content.messageBox.add_child(this._errorMessageLabel); | ||||
|         this._errorMessageLabel.hide(); | ||||
|  | ||||
|         this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' }); | ||||
| @@ -137,15 +136,30 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|  | ||||
|         this._cancelButton = this.addButton({ label: _("Cancel"), | ||||
|                                               action: this.cancel.bind(this), | ||||
|                                               key: Clutter.Escape }); | ||||
|                                               key: Clutter.KEY_Escape }); | ||||
|         this._okButton = this.addButton({ label: _("Authenticate"), | ||||
|                                           action: this._onAuthenticateButtonPressed.bind(this), | ||||
|                                           default: true }); | ||||
|                                           reactive: false }); | ||||
|         this._okButton.bind_property('reactive', | ||||
|             this._okButton, 'can-focus', | ||||
|             GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|         this._passwordEntry.clutter_text.connect('text-changed', text => { | ||||
|             this._okButton.reactive = text.get_text().length > 0; | ||||
|         }); | ||||
|  | ||||
|         this._doneEmitted = false; | ||||
|  | ||||
|         this._mode = -1; | ||||
|  | ||||
|         this._identityToAuth = Polkit.UnixUser.new_for_name(userName); | ||||
|         this._cookie = cookie; | ||||
|  | ||||
|         this._userLoadedId = this._user.connect('notify::is-loaded', | ||||
|             this._onUserChanged.bind(this)); | ||||
|         this._userChangedId = this._user.connect('changed', | ||||
|             this._onUserChanged.bind(this)); | ||||
|         this._onUserChanged(); | ||||
|     } | ||||
|  | ||||
|     _setWorking(working) { | ||||
| @@ -155,8 +169,9 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|             this._workSpinner.stop(); | ||||
|     } | ||||
|  | ||||
|     performAuthentication() { | ||||
|         this._destroySession(); | ||||
|     _initiateSession() { | ||||
|         this._destroySession(DELAYED_RESET_TIMEOUT); | ||||
|  | ||||
|         this._session = new PolkitAgent.Session({ identity: this._identityToAuth, | ||||
|                                                   cookie: this._cookie }); | ||||
|         this._sessionCompletedId = this._session.connect('completed', this._onSessionCompleted.bind(this)); | ||||
| @@ -195,18 +210,15 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _updateSensitivity(sensitive) { | ||||
|         this._passwordEntry.reactive = sensitive; | ||||
|         this._passwordEntry.clutter_text.editable = sensitive; | ||||
|  | ||||
|         this._okButton.can_focus = sensitive; | ||||
|         this._okButton.reactive = sensitive; | ||||
|         this._setWorking(!sensitive); | ||||
|     } | ||||
|  | ||||
|     _onEntryActivate() { | ||||
|         let response = this._passwordEntry.get_text(); | ||||
|         this._updateSensitivity(false); | ||||
|         if (response.length === 0) | ||||
|             return; | ||||
|  | ||||
|         this._passwordEntry.reactive = false; | ||||
|         this._okButton.reactive = false; | ||||
|         this._setWorking(true); | ||||
|  | ||||
|         this._session.response(response); | ||||
|         // When the user responds, dismiss already shown info and | ||||
|         // error texts (if any) | ||||
| @@ -216,7 +228,10 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     _onAuthenticateButtonPressed() { | ||||
|         this._onEntryActivate(); | ||||
|         if (this._mode === DialogMode.CONFIRM) | ||||
|             this._initiateSession(); | ||||
|         else | ||||
|             this._onEntryActivate(); | ||||
|     } | ||||
|  | ||||
|     _onSessionCompleted(session, gainedAuthorization) { | ||||
| @@ -235,7 +250,7 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|              * error providing authentication-method specific information), | ||||
|              * show "Sorry, that didn't work. Please try again." | ||||
|              */ | ||||
|             if (!this._errorMessageLabel.visible && !this._wasDismissed) { | ||||
|             if (!this._errorMessageLabel.visible) { | ||||
|                 /* Translators: "that didn't work" refers to the fact that the | ||||
|                  * requested authentication was not gained; this can happen | ||||
|                  * because of an authentication error (like invalid password), | ||||
| @@ -247,11 +262,16 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|             } | ||||
|  | ||||
|             /* Try and authenticate again */ | ||||
|             this.performAuthentication(); | ||||
|             this._initiateSession(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _onSessionRequest(session, request, echoOn) { | ||||
|         if (this._sessionRequestTimeoutId) { | ||||
|             GLib.source_remove(this._sessionRequestTimeoutId); | ||||
|             this._sessionRequestTimeoutId = 0; | ||||
|         } | ||||
|  | ||||
|         // Cheap localization trick | ||||
|         if (request == 'Password:' || request == 'Password: ') | ||||
|             this._passwordLabel.set_text(_("Password:")); | ||||
| @@ -265,9 +285,12 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|  | ||||
|         this._passwordBox.show(); | ||||
|         this._passwordEntry.set_text(''); | ||||
|         this._passwordEntry.grab_key_focus(); | ||||
|         this._updateSensitivity(true); | ||||
|         this._passwordEntry.reactive  = true; | ||||
|         this._okButton.reactive = false; | ||||
|         this._setWorking(false); | ||||
|  | ||||
|         this._ensureOpen(); | ||||
|         this._passwordEntry.grab_key_focus(); | ||||
|     } | ||||
|  | ||||
|     _onSessionShowError(session, text) { | ||||
| @@ -288,29 +311,78 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|         this._ensureOpen(); | ||||
|     } | ||||
|  | ||||
|     _destroySession() { | ||||
|     _destroySession(delay = 0) { | ||||
|         if (this._session) { | ||||
|             if (!this._completed) | ||||
|                 this._session.cancel(); | ||||
|             this._completed = false; | ||||
|  | ||||
|             this._session.disconnect(this._sessionCompletedId); | ||||
|             this._session.disconnect(this._sessionRequestId); | ||||
|             this._session.disconnect(this._sessionShowErrorId); | ||||
|             this._session.disconnect(this._sessionShowInfoId); | ||||
|  | ||||
|             if (!this._completed) | ||||
|                 this._session.cancel(); | ||||
|  | ||||
|             this._completed = false; | ||||
|             this._session = null; | ||||
|         } | ||||
|  | ||||
|         if (this._sessionRequestTimeoutId) { | ||||
|             GLib.source_remove(this._sessionRequestTimeoutId); | ||||
|             this._sessionRequestTimeoutId = 0; | ||||
|         } | ||||
|  | ||||
|         let resetDialog = () => { | ||||
|             if (this.state != ModalDialog.State.OPENED) | ||||
|                 return; | ||||
|  | ||||
|             this._passwordBox.hide(); | ||||
|             this._cancelButton.grab_key_focus(); | ||||
|             this._okButton.reactive = false; | ||||
|         }; | ||||
|  | ||||
|         if (delay) { | ||||
|             this._sessionRequestTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, resetDialog); | ||||
|             GLib.Source.set_name_by_id(this._sessionRequestTimeoutId, '[gnome-shell] this._sessionRequestTimeoutId'); | ||||
|         } else { | ||||
|             resetDialog(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _onUserChanged() { | ||||
|         if (this._user.is_loaded && this._userAvatar) { | ||||
|             this._userAvatar.update(); | ||||
|             this._userAvatar.actor.show(); | ||||
|         if (!this._user.is_loaded) | ||||
|             return; | ||||
|  | ||||
|         let userName = this._user.get_user_name(); | ||||
|         let realName = this._user.get_real_name(); | ||||
|  | ||||
|         if (userName !== 'root') | ||||
|             this._userLabel.set_text(realName); | ||||
|  | ||||
|         this._userAvatar.update(); | ||||
|  | ||||
|         if (this._user.get_password_mode() === AccountsService.UserPasswordMode.NONE) { | ||||
|             if (this._mode === DialogMode.CONFIRM) | ||||
|                 return; | ||||
|  | ||||
|             this._mode = DialogMode.CONFIRM; | ||||
|             this._destroySession(); | ||||
|  | ||||
|             this._okButton.reactive = true; | ||||
|  | ||||
|             /* We normally open the dialog when we get a "request" signal, but | ||||
|              * since in this case initiating a session would perform the | ||||
|              * authentication, only open the dialog and initiate the session | ||||
|              * when the user confirmed. */ | ||||
|             this._ensureOpen(); | ||||
|         } else { | ||||
|             if (this._mode === DialogMode.AUTH) | ||||
|                 return; | ||||
|  | ||||
|             this._mode = DialogMode.AUTH; | ||||
|             this._initiateSession(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     cancel() { | ||||
|         this._wasDismissed = true; | ||||
|         this.close(global.get_current_time()); | ||||
|         this._emitDone(true); | ||||
|     } | ||||
| @@ -318,7 +390,10 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|     _onDialogClosed() { | ||||
|         if (this._sessionUpdatedId) | ||||
|             Main.sessionMode.disconnect(this._sessionUpdatedId); | ||||
|         this._sessionUpdatedId = 0; | ||||
|  | ||||
|         if (this._sessionRequestTimeoutId) | ||||
|             GLib.source_remove(this._sessionRequestTimeoutId); | ||||
|         this._sessionRequestTimeoutId = 0; | ||||
|  | ||||
|         if (this._user) { | ||||
|             this._user.disconnect(this._userLoadedId); | ||||
| @@ -330,19 +405,20 @@ var AuthenticationDialog = GObject.registerClass({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var AuthenticationAgent = class { | ||||
|     constructor() { | ||||
| var AuthenticationAgent = GObject.registerClass( | ||||
| class AuthenticationAgent extends Shell.PolkitAuthenticationAgent { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|  | ||||
|         this._currentDialog = null; | ||||
|         this._handle = null; | ||||
|         this._native = new Shell.PolkitAuthenticationAgent(); | ||||
|         this._native.connect('initiate', this._onInitiate.bind(this)); | ||||
|         this._native.connect('cancel', this._onCancel.bind(this)); | ||||
|         this.connect('initiate', this._onInitiate.bind(this)); | ||||
|         this.connect('cancel', this._onCancel.bind(this)); | ||||
|         this._sessionUpdatedId = 0; | ||||
|     } | ||||
|  | ||||
|     enable() { | ||||
|         try { | ||||
|             this._native.register(); | ||||
|             this.register(); | ||||
|         } catch (e) { | ||||
|             log('Failed to register AuthenticationAgent'); | ||||
|         } | ||||
| @@ -350,7 +426,7 @@ var AuthenticationAgent = class { | ||||
|  | ||||
|     disable() { | ||||
|         try { | ||||
|             this._native.unregister(); | ||||
|             this.unregister(); | ||||
|         } catch (e) { | ||||
|             log('Failed to unregister AuthenticationAgent'); | ||||
|         } | ||||
| @@ -369,19 +445,7 @@ var AuthenticationAgent = class { | ||||
|         } | ||||
|  | ||||
|         this._currentDialog = new AuthenticationDialog(actionId, message, cookie, userNames); | ||||
|  | ||||
|         // We actually don't want to open the dialog until we know for | ||||
|         // sure that we're going to interact with the user. For | ||||
|         // example, if the password for the identity to auth is blank | ||||
|         // (which it will be on a live CD) then there will be no | ||||
|         // conversation at all... of course, we don't *know* that | ||||
|         // until we actually try it. | ||||
|         // | ||||
|         // See https://bugzilla.gnome.org/show_bug.cgi?id=643062 for more | ||||
|         // discussion. | ||||
|  | ||||
|         this._currentDialog.connect('done', this._onDialogDone.bind(this)); | ||||
|         this._currentDialog.performAuthentication(); | ||||
|     } | ||||
|  | ||||
|     _onCancel(_nativeAgent) { | ||||
| @@ -400,8 +464,8 @@ var AuthenticationAgent = class { | ||||
|             Main.sessionMode.disconnect(this._sessionUpdatedId); | ||||
|         this._sessionUpdatedId = 0; | ||||
|  | ||||
|         this._native.complete(dismissed); | ||||
|         this.complete(dismissed); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var Component = AuthenticationAgent; | ||||
|   | ||||
| @@ -19,7 +19,7 @@ const MessageTray = imports.ui.messageTray; | ||||
| const Params = imports.misc.params; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const HAVE_TP = (Tp != null && Tpl != null); | ||||
| const HAVE_TP = Tp != null && Tpl != null; | ||||
|  | ||||
| // See Notification.appendMessage | ||||
| var SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes | ||||
| @@ -37,7 +37,7 @@ var CHAT_EXPAND_LINES = 12; | ||||
|  | ||||
| var NotificationDirection = { | ||||
|     SENT: 'chat-sent', | ||||
|     RECEIVED: 'chat-received' | ||||
|     RECEIVED: 'chat-received', | ||||
| }; | ||||
|  | ||||
| function makeMessageFromTpMessage(tpMessage, direction) { | ||||
| @@ -49,10 +49,10 @@ function makeMessageFromTpMessage(tpMessage, direction) { | ||||
|  | ||||
|     return { | ||||
|         messageType: tpMessage.get_message_type(), | ||||
|         text: text, | ||||
|         text, | ||||
|         sender: tpMessage.sender.alias, | ||||
|         timestamp: timestamp, | ||||
|         direction: direction | ||||
|         timestamp, | ||||
|         direction, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -66,7 +66,7 @@ function makeMessageFromTplEvent(event) { | ||||
|         text: event.get_message(), | ||||
|         sender: event.get_sender().get_alias(), | ||||
|         timestamp: event.get_timestamp(), | ||||
|         direction: direction | ||||
|         direction, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -158,7 +158,7 @@ class TelepathyClient extends Tp.BaseClient { | ||||
|                 continue; | ||||
|  | ||||
|             /* Only observe contact text channels */ | ||||
|             if ((!(channel instanceof Tp.TextChannel)) || | ||||
|             if (!(channel instanceof Tp.TextChannel) || | ||||
|                targetHandleType != Tp.HandleType.CONTACT) | ||||
|                 continue; | ||||
|  | ||||
| @@ -215,7 +215,7 @@ class TelepathyClient extends Tp.BaseClient { | ||||
|                 // We are already handling the channel, display the source | ||||
|                 let source = this._chatSources[channel.get_object_path()]; | ||||
|                 if (source) | ||||
|                     source.notify(); | ||||
|                     source.showNotification(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -231,11 +231,12 @@ class TelepathyClient extends Tp.BaseClient { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT) | ||||
|         if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT) { | ||||
|             this._approveTextChannel(account, conn, channel, dispatchOp, context); | ||||
|         else | ||||
|         } else { | ||||
|             context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT, | ||||
|                                         message: 'Unsupported channel type' })); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _approveTextChannel(account, conn, channel, dispatchOp, context) { | ||||
| @@ -248,7 +249,7 @@ class TelepathyClient extends Tp.BaseClient { | ||||
|         } | ||||
|  | ||||
|         // Approve private text channels right away as we are going to handle it | ||||
|         dispatchOp.claim_with_async(this, (dispatchOp, result) => { | ||||
|         dispatchOp.claim_with_async(this, (o, result) => { | ||||
|             try { | ||||
|                 dispatchOp.claim_with_finish(result); | ||||
|                 this._handlingChannels(account, conn, [channel], false); | ||||
| @@ -266,9 +267,10 @@ class TelepathyClient extends Tp.BaseClient { | ||||
|     } | ||||
| }) : null; | ||||
|  | ||||
| var ChatSource = class extends MessageTray.Source { | ||||
|     constructor(account, conn, channel, contact, client) { | ||||
|         super(contact.get_alias()); | ||||
| var ChatSource = HAVE_TP ? GObject.registerClass( | ||||
| class ChatSource extends MessageTray.Source { | ||||
|     _init(account, conn, channel, contact, client) { | ||||
|         super._init(contact.get_alias()); | ||||
|  | ||||
|         this._account = account; | ||||
|         this._contact = contact; | ||||
| @@ -326,7 +328,7 @@ var ChatSource = class extends MessageTray.Source { | ||||
|  | ||||
|         // We ack messages when the user expands the new notification | ||||
|         let id = this._banner.connect('expanded', this._ackMessages.bind(this)); | ||||
|         this._banner.actor.connect('destroy', () => { | ||||
|         this._banner.connect('destroy', () => { | ||||
|             this._banner.disconnect(id); | ||||
|             this._banner = null; | ||||
|         }); | ||||
| @@ -348,11 +350,10 @@ var ChatSource = class extends MessageTray.Source { | ||||
|  | ||||
|     getIcon() { | ||||
|         let file = this._contact.get_avatar_file(); | ||||
|         if (file) { | ||||
|             return new Gio.FileIcon({ file: file }); | ||||
|         } else { | ||||
|         if (file) | ||||
|             return new Gio.FileIcon({ file }); | ||||
|         else | ||||
|             return new Gio.ThemedIcon({ name: 'avatar-default' }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     getSecondaryIcon() { | ||||
| @@ -386,10 +387,11 @@ var ChatSource = class extends MessageTray.Source { | ||||
|  | ||||
|     _updateAvatarIcon() { | ||||
|         this.iconUpdated(); | ||||
|         if (this._notifiction) | ||||
|         if (this._notifiction) { | ||||
|             this._notification.update(this._notification.title, | ||||
|                                       this._notification.bannerBodyText, | ||||
|                                       { gicon: this.getIcon() }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     open() { | ||||
| @@ -476,7 +478,7 @@ var ChatSource = class extends MessageTray.Source { | ||||
|             this._notification.appendMessage(pendingMessages[i], true); | ||||
|  | ||||
|         if (pendingMessages.length > 0) | ||||
|             this.notify(); | ||||
|             this.showNotification(); | ||||
|     } | ||||
|  | ||||
|     destroy(reason) { | ||||
| @@ -553,7 +555,7 @@ var ChatSource = class extends MessageTray.Source { | ||||
|  | ||||
|     _notifyTimeout() { | ||||
|         if (this._pendingMessages.length != 0) | ||||
|             this.notify(); | ||||
|             this.showNotification(); | ||||
|  | ||||
|         this._notifyTimeoutId = 0; | ||||
|  | ||||
| @@ -568,8 +570,8 @@ var ChatSource = class extends MessageTray.Source { | ||||
|         this._notification.appendMessage(message); | ||||
|     } | ||||
|  | ||||
|     notify() { | ||||
|         super.notify(this._notification); | ||||
|     showNotification() { | ||||
|         super.showNotification(this._notification); | ||||
|     } | ||||
|  | ||||
|     respond(text) { | ||||
| @@ -601,10 +603,11 @@ var ChatSource = class extends MessageTray.Source { | ||||
|     } | ||||
|  | ||||
|     _presenceChanged(_contact, _presence, _status, _message) { | ||||
|         if (this._notification) | ||||
|         if (this._notification) { | ||||
|             this._notification.update(this._notification.title, | ||||
|                                       this._notification.bannerBodyText, | ||||
|                                       { secondaryGIcon: this.getSecondaryIcon() }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _pendingRemoved(channel, message) { | ||||
| @@ -625,12 +628,18 @@ var ChatSource = class extends MessageTray.Source { | ||||
|         // 'pending-message-removed' for each one. | ||||
|         this._channel.ack_all_pending_messages_async(null); | ||||
|     } | ||||
| }; | ||||
| }) : null; | ||||
|  | ||||
| var ChatNotification = class extends MessageTray.Notification { | ||||
|     constructor(source) { | ||||
|         super(source, source.title, null, | ||||
|               { secondaryGIcon: source.getSecondaryIcon() }); | ||||
| var ChatNotification = HAVE_TP ? GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'message-removed': { param_types: [Tp.Message.$gtype] }, | ||||
|         'message-added': { param_types: [Tp.Message.$gtype] }, | ||||
|         'timestamp-changed': { param_types: [Tp.Message.$gtype] }, | ||||
|     }, | ||||
| }, class ChatNotification extends MessageTray.Notification { | ||||
|     _init(source) { | ||||
|         super._init(source, source.title, null, | ||||
|             { secondaryGIcon: source.getSecondaryIcon() }); | ||||
|         this.setUrgency(MessageTray.Urgency.HIGH); | ||||
|         this.setResident(true); | ||||
|  | ||||
| @@ -647,16 +656,16 @@ var ChatNotification = class extends MessageTray.Notification { | ||||
|  | ||||
|     /** | ||||
|      * appendMessage: | ||||
|      * @message: An object with the properties: | ||||
|      *   text: the body of the message, | ||||
|      *   messageType: a #Tp.ChannelTextMessageType, | ||||
|      *   sender: the name of the sender, | ||||
|      *   timestamp: the time the message was sent | ||||
|      *   direction: a #NotificationDirection | ||||
|      * @param {Object} message: An object with the properties | ||||
|      *   {string} message.text: the body of the message, | ||||
|      *   {Tp.ChannelTextMessageType} message.messageType: the type | ||||
|      *   {string} message.sender: the name of the sender, | ||||
|      *   {number} message.timestamp: the time the message was sent | ||||
|      *   {NotificationDirection} message.direction: a #NotificationDirection | ||||
|      * | ||||
|      * @noTimestamp: Whether to add a timestamp. If %true, no timestamp | ||||
|      *   will be added, regardless of the difference since the | ||||
|      *   last timestamp | ||||
|      * @param {bool} noTimestamp: Whether to add a timestamp. If %true, | ||||
|      *   no timestamp will be added, regardless of the difference since | ||||
|      *   the last timestamp | ||||
|      */ | ||||
|     appendMessage(message, noTimestamp) { | ||||
|         let messageBody = GLib.markup_escape_text(message.text, -1); | ||||
| @@ -668,19 +677,20 @@ var ChatNotification = class extends MessageTray.Notification { | ||||
|             styles.push('chat-action'); | ||||
|         } | ||||
|  | ||||
|         if (message.direction == NotificationDirection.RECEIVED) | ||||
|         if (message.direction == NotificationDirection.RECEIVED) { | ||||
|             this.update(this.source.title, messageBody, | ||||
|                         { datetime: GLib.DateTime.new_from_unix_local (message.timestamp), | ||||
|                         { datetime: GLib.DateTime.new_from_unix_local(message.timestamp), | ||||
|                           bannerMarkup: true }); | ||||
|         } | ||||
|  | ||||
|         let group = (message.direction == NotificationDirection.RECEIVED | ||||
|             ? 'received' : 'sent'); | ||||
|         let group = message.direction == NotificationDirection.RECEIVED | ||||
|             ? 'received' : 'sent'; | ||||
|  | ||||
|         this._append({ body: messageBody, | ||||
|                        group: group, | ||||
|                        styles: styles, | ||||
|                        group, | ||||
|                        styles, | ||||
|                        timestamp: message.timestamp, | ||||
|                        noTimestamp: noTimestamp }); | ||||
|                        noTimestamp }); | ||||
|     } | ||||
|  | ||||
|     _filterMessages() { | ||||
| @@ -688,7 +698,7 @@ var ChatNotification = class extends MessageTray.Notification { | ||||
|             return; | ||||
|  | ||||
|         let lastMessageTime = this.messages[0].timestamp; | ||||
|         let currentTime = (Date.now() / 1000); | ||||
|         let currentTime = Date.now() / 1000; | ||||
|  | ||||
|         // Keep the scrollback from growing too long. If the most | ||||
|         // recent message (before the one we just added) is within | ||||
| @@ -696,7 +706,7 @@ var ChatNotification = class extends MessageTray.Notification { | ||||
|         // SCROLLBACK_RECENT_LENGTH previous messages. Otherwise | ||||
|         // we'll keep SCROLLBACK_IDLE_LENGTH messages. | ||||
|  | ||||
|         let maxLength = (lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME) | ||||
|         let maxLength = lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME | ||||
|             ? SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH; | ||||
|  | ||||
|         let filteredHistory = this.messages.filter(item => item.realMessage); | ||||
| @@ -710,16 +720,16 @@ var ChatNotification = class extends MessageTray.Notification { | ||||
|  | ||||
|     /** | ||||
|      * _append: | ||||
|      * @props: An object with the properties: | ||||
|      *  body: The text of the message. | ||||
|      *  group: The group of the message, one of: | ||||
|      * @param {Object} props: An object with the properties: | ||||
|      *  {string} props.body: The text of the message. | ||||
|      *  {string} props.group: The group of the message, one of: | ||||
|      *         'received', 'sent', 'meta'. | ||||
|      *  styles: Style class names for the message to have. | ||||
|      *  timestamp: The timestamp of the message. | ||||
|      *  noTimestamp: suppress timestamp signal? | ||||
|      *  {string[]} props.styles: Style class names for the message to have. | ||||
|      *  {number} props.timestamp: The timestamp of the message. | ||||
|      *  {bool} props.noTimestamp: suppress timestamp signal? | ||||
|      */ | ||||
|     _append(props) { | ||||
|         let currentTime = (Date.now() / 1000); | ||||
|         let currentTime = Date.now() / 1000; | ||||
|         props = Params.parse(props, { body: null, | ||||
|                                       group: null, | ||||
|                                       styles: [], | ||||
| @@ -782,7 +792,7 @@ var ChatNotification = class extends MessageTray.Notification { | ||||
|  | ||||
|         this._filterMessages(); | ||||
|     } | ||||
| }; | ||||
| }) : null; | ||||
|  | ||||
| var ChatLineBox = GObject.registerClass( | ||||
| class ChatLineBox extends St.BoxLayout { | ||||
| @@ -792,9 +802,10 @@ class ChatLineBox extends St.BoxLayout { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var ChatNotificationBanner = class extends MessageTray.NotificationBanner { | ||||
|     constructor(notification) { | ||||
|         super(notification); | ||||
| var ChatNotificationBanner = GObject.registerClass( | ||||
| class ChatNotificationBanner extends MessageTray.NotificationBanner { | ||||
|     _init(notification) { | ||||
|         super._init(notification); | ||||
|  | ||||
|         this._responseEntry = new St.Entry({ style_class: 'chat-response', | ||||
|                                              x_expand: true, | ||||
| @@ -879,8 +890,7 @@ var ChatNotificationBanner = class extends MessageTray.NotificationBanner { | ||||
|     } | ||||
|  | ||||
|     _addMessage(message) { | ||||
|         let highlighter = new MessageList.URLHighlighter(message.body, true, true); | ||||
|         let body = highlighter.actor; | ||||
|         let body = new MessageList.URLHighlighter(message.body, true, true); | ||||
|  | ||||
|         let styles = message.styles; | ||||
|         for (let i = 0; i < styles.length; i++) | ||||
| @@ -968,6 +978,6 @@ var ChatNotificationBanner = class extends MessageTray.NotificationBanner { | ||||
|             this.notification.source.setChatState(Tp.ChannelChatState.ACTIVE); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var Component = TelepathyComponent; | ||||
|   | ||||
| @@ -12,7 +12,7 @@ var POPUP_APPICON_SIZE = 96; | ||||
| var SortGroup = { | ||||
|     TOP:    0, | ||||
|     MIDDLE: 1, | ||||
|     BOTTOM: 2 | ||||
|     BOTTOM: 2, | ||||
| }; | ||||
|  | ||||
| var CtrlAltTabManager = class CtrlAltTabManager { | ||||
| @@ -86,25 +86,26 @@ var CtrlAltTabManager = class CtrlAltTabManager { | ||||
|             for (let i = 0; i < windows.length; i++) { | ||||
|                 let icon = null; | ||||
|                 let iconName = null; | ||||
|                 if (windows[i].get_window_type () == Meta.WindowType.DESKTOP) { | ||||
|                 if (windows[i].get_window_type() == Meta.WindowType.DESKTOP) { | ||||
|                     iconName = 'video-display-symbolic'; | ||||
|                 } else { | ||||
|                     let app = windowTracker.get_window_app(windows[i]); | ||||
|                     if (app) | ||||
|                     if (app) { | ||||
|                         icon = app.create_icon_texture(POPUP_APPICON_SIZE); | ||||
|                     else | ||||
|                     } else { | ||||
|                         icon = textureCache.bind_cairo_surface_property(windows[i], | ||||
|                                                                         'icon', | ||||
|                                                                         POPUP_APPICON_SIZE); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 items.push({ name: windows[i].title, | ||||
|                              proxy: windows[i].get_compositor_private(), | ||||
|                              focusCallback: function(timestamp) { | ||||
|                                  Main.activateWindow(this, timestamp); | ||||
|                              }.bind(windows[i]), | ||||
|                              focusCallback: timestamp => { | ||||
|                                  Main.activateWindow(windows[i], timestamp); | ||||
|                              }, | ||||
|                              iconActor: icon, | ||||
|                              iconName: iconName, | ||||
|                              iconName, | ||||
|                              sortGroup: SortGroup.MIDDLE }); | ||||
|             } | ||||
|         } | ||||
| @@ -143,9 +144,9 @@ class CtrlAltTabPopup extends SwitcherPopup.SwitcherPopup { | ||||
|             this._select(this._next()); | ||||
|         else if (action == Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD) | ||||
|             this._select(this._previous()); | ||||
|         else if (keysym == Clutter.Left) | ||||
|         else if (keysym == Clutter.KEY_Left) | ||||
|             this._select(this._previous()); | ||||
|         else if (keysym == Clutter.Right) | ||||
|         else if (keysym == Clutter.KEY_Right) | ||||
|             this._select(this._next()); | ||||
|         else | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
| @@ -177,10 +178,13 @@ class CtrlAltTabSwitcher extends SwitcherPopup.SwitcherList { | ||||
|             icon = new St.Icon({ icon_name: item.iconName, | ||||
|                                  icon_size: POPUP_APPICON_SIZE }); | ||||
|         } | ||||
|         box.add(icon, { x_fill: false, y_fill: false } ); | ||||
|         box.add_child(icon); | ||||
|  | ||||
|         let text = new St.Label({ text: item.name }); | ||||
|         box.add(text, { x_fill: false }); | ||||
|         let text = new St.Label({ | ||||
|             text: item.name, | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         box.add_child(text); | ||||
|  | ||||
|         this.addItem(box, text); | ||||
|     } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported Dash */ | ||||
|  | ||||
| const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
| const { Clutter, GLib, GObject, | ||||
|         Graphene, Meta, Shell, St } = imports.gi; | ||||
|  | ||||
| const AppDisplay = imports.ui.appDisplay; | ||||
| const AppFavorites = imports.ui.appFavorites; | ||||
| @@ -16,18 +16,18 @@ var DASH_ITEM_LABEL_HIDE_TIME = 100; | ||||
| var DASH_ITEM_HOVER_TIMEOUT = 300; | ||||
|  | ||||
| function getAppFromSource(source) { | ||||
|     if (source instanceof AppDisplay.AppIcon) { | ||||
|     if (source instanceof AppDisplay.AppIcon) | ||||
|         return source.app; | ||||
|     } else { | ||||
|     else | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|  | ||||
| var DashIcon = class DashIcon extends AppDisplay.AppIcon { | ||||
|     constructor(app) { | ||||
|         super(app, { | ||||
| var DashIcon = GObject.registerClass( | ||||
| class DashIcon extends AppDisplay.AppIcon { | ||||
|     _init(app) { | ||||
|         super._init(app, { | ||||
|             setSizeManually: true, | ||||
|             showLabel: false | ||||
|             showLabel: false, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -45,7 +45,7 @@ var DashIcon = class DashIcon extends AppDisplay.AppIcon { | ||||
|     acceptDrop() { | ||||
|         return false; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| // A container like StBin, but taking the child's scale into account | ||||
| // when requesting a size | ||||
| @@ -53,7 +53,7 @@ var DashItemContainer = GObject.registerClass( | ||||
| class DashItemContainer extends St.Widget { | ||||
|     _init() { | ||||
|         super._init({ style_class: 'dash-item-container', | ||||
|                       pivot_point: new Clutter.Point({ x: .5, y: .5 }), | ||||
|                       pivot_point: new Graphene.Point({ x: .5, y: .5 }), | ||||
|                       scale_x: 0, | ||||
|                       scale_y: 0, | ||||
|                       opacity: 0, | ||||
| @@ -125,7 +125,7 @@ class DashItemContainer extends St.Widget { | ||||
|         this.label.ease({ | ||||
|             opacity: 255, | ||||
|             duration: DASH_ITEM_LABEL_SHOW_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -139,7 +139,7 @@ class DashItemContainer extends St.Widget { | ||||
|             opacity: 0, | ||||
|             duration: DASH_ITEM_LABEL_HIDE_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this.label.hide() | ||||
|             onComplete: () => this.label.hide(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -163,7 +163,7 @@ class DashItemContainer extends St.Widget { | ||||
|             scale_y: 1, | ||||
|             opacity: 255, | ||||
|             duration: time, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -182,7 +182,7 @@ class DashItemContainer extends St.Widget { | ||||
|             opacity: 0, | ||||
|             duration: DASH_ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this.destroy() | ||||
|             onComplete: () => this.destroy(), | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
| @@ -284,9 +284,12 @@ var DashActor = GObject.registerClass( | ||||
| class DashActor extends St.Widget { | ||||
|     _init() { | ||||
|         let layout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         super._init({ name: 'dash', | ||||
|                       layout_manager: layout, | ||||
|                       clip_to_allocation: true }); | ||||
|         super._init({ | ||||
|             name: 'dash', | ||||
|             layout_manager: layout, | ||||
|             clip_to_allocation: true, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     vfunc_allocate(box, flags) { | ||||
| @@ -330,8 +333,10 @@ class DashActor extends St.Widget { | ||||
|  | ||||
| const baseIconSizes = [16, 22, 24, 32, 48, 64]; | ||||
|  | ||||
| var Dash = class Dash { | ||||
|     constructor() { | ||||
| var Dash = GObject.registerClass({ | ||||
|     Signals: { 'icon-size-changed': {} }, | ||||
| }, class Dash extends St.Bin { | ||||
|     _init() { | ||||
|         this._maxHeight = -1; | ||||
|         this.iconSize = 64; | ||||
|         this._shownInitially = false; | ||||
| @@ -359,11 +364,11 @@ var Dash = class Dash { | ||||
|  | ||||
|         this._container.add_actor(this._showAppsIcon); | ||||
|  | ||||
|         this.actor = new St.Bin({ child: this._container }); | ||||
|         this.actor.connect('notify::height', () => { | ||||
|             if (this._maxHeight != this.actor.height) | ||||
|         super._init({ child: this._container }); | ||||
|         this.connect('notify::height', () => { | ||||
|             if (this._maxHeight != this.height) | ||||
|                 this._queueRedisplay(); | ||||
|             this._maxHeight = this.actor.height; | ||||
|             this._maxHeight = this.height; | ||||
|         }); | ||||
|  | ||||
|         this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this)); | ||||
| @@ -386,13 +391,13 @@ var Dash = class Dash { | ||||
|  | ||||
|         // Translators: this is the name of the dock/favorites area on | ||||
|         // the left of the overview | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic'); | ||||
|         Main.ctrlAltTabManager.addGroup(this, _("Dash"), 'user-bookmarks-symbolic'); | ||||
|     } | ||||
|  | ||||
|     _onDragBegin() { | ||||
|         this._dragCancelled = false; | ||||
|         this._dragMonitor = { | ||||
|             dragMotion: this._onDragMotion.bind(this) | ||||
|             dragMotion: this._onDragMotion.bind(this), | ||||
|         }; | ||||
|         DND.addDragMonitor(this._dragMonitor); | ||||
|  | ||||
| @@ -476,16 +481,16 @@ var Dash = class Dash { | ||||
|         let appIcon = new DashIcon(app); | ||||
|  | ||||
|         appIcon.connect('menu-state-changed', | ||||
|                         (appIcon, opened) => { | ||||
|                         (o, opened) => { | ||||
|                             this._itemMenuStateChanged(item, opened); | ||||
|                         }); | ||||
|  | ||||
|         let item = new DashItemContainer(); | ||||
|         item.setChild(appIcon.actor); | ||||
|         item.setChild(appIcon); | ||||
|  | ||||
|         // Override default AppIcon label_actor, now the | ||||
|         // accessible_name is set at DashItemContainer.setLabelText | ||||
|         appIcon.actor.label_actor = null; | ||||
|         appIcon.label_actor = null; | ||||
|         item.setLabelText(app.get_name()); | ||||
|  | ||||
|         appIcon.icon.setIconSize(this.iconSize); | ||||
| @@ -624,7 +629,7 @@ var Dash = class Dash { | ||||
|                 width: targetWidth, | ||||
|                 height: targetHeight, | ||||
|                 duration: DASH_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -723,9 +728,10 @@ var Dash = class Dash { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < addedItems.length; i++) | ||||
|         for (let i = 0; i < addedItems.length; i++) { | ||||
|             this._box.insert_child_at_index(addedItems[i].item, | ||||
|                                             addedItems[i].pos); | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < removedActors.length; i++) { | ||||
|             let item = removedActors[i]; | ||||
| @@ -749,9 +755,8 @@ var Dash = class Dash { | ||||
|         if (!this._shownInitially) | ||||
|             this._shownInitially = true; | ||||
|  | ||||
|         for (let i = 0; i < addedItems.length; i++) { | ||||
|         for (let i = 0; i < addedItems.length; i++) | ||||
|             addedItems[i].item.show(animate); | ||||
|         } | ||||
|  | ||||
|         // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 | ||||
|         // Without it, StBoxLayout may use a stale size cache | ||||
| @@ -831,8 +836,8 @@ var Dash = class Dash { | ||||
|             } | ||||
|  | ||||
|             this._dragPlaceholder = new DragPlaceholderItem(); | ||||
|             this._dragPlaceholder.child.set_width (this.iconSize); | ||||
|             this._dragPlaceholder.child.set_height (this.iconSize / 2); | ||||
|             this._dragPlaceholder.child.set_width(this.iconSize); | ||||
|             this._dragPlaceholder.child.set_height(this.iconSize / 2); | ||||
|             this._box.insert_child_at_index(this._dragPlaceholder, | ||||
|                                             this._dragPlaceholderPos); | ||||
|             this._dragPlaceholder.show(fadeIn); | ||||
| @@ -846,7 +851,7 @@ var Dash = class Dash { | ||||
|         if (!this._dragPlaceholder) | ||||
|             return DND.DragMotionResult.NO_DROP; | ||||
|  | ||||
|         let srcIsFavorite = (favPos != -1); | ||||
|         let srcIsFavorite = favPos != -1; | ||||
|  | ||||
|         if (srcIsFavorite) | ||||
|             return DND.DragMotionResult.MOVE_DROP; | ||||
| @@ -859,9 +864,8 @@ var Dash = class Dash { | ||||
|         let app = getAppFromSource(source); | ||||
|  | ||||
|         // Don't allow favoriting of transient apps | ||||
|         if (app == null || app.is_window_backed()) { | ||||
|         if (app == null || app.is_window_backed()) | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!global.settings.is_writable('favorite-apps')) | ||||
|             return false; | ||||
| @@ -870,7 +874,7 @@ var Dash = class Dash { | ||||
|  | ||||
|         let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); | ||||
|  | ||||
|         let srcIsFavorite = (id in favorites); | ||||
|         let srcIsFavorite = id in favorites; | ||||
|  | ||||
|         let favPos = 0; | ||||
|         let children = this._box.get_children(); | ||||
| @@ -902,5 +906,4 @@ var Dash = class Dash { | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Dash.prototype); | ||||
| }); | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| /* exported DateMenuButton */ | ||||
|  | ||||
| const { Clutter, Gio, GLib, GnomeDesktop, | ||||
|         GObject, GWeather, Shell, St } = imports.gi; | ||||
|         GObject, GWeather, Pango, Shell, St } = imports.gi; | ||||
|  | ||||
| const Util = imports.misc.util; | ||||
| const Main = imports.ui.main; | ||||
| @@ -25,24 +25,25 @@ function _isToday(date) { | ||||
|            now.getDate() == date.getDate(); | ||||
| } | ||||
|  | ||||
| var TodayButton = class TodayButton { | ||||
|     constructor(calendar) { | ||||
| function _gDateTimeToDate(datetime) { | ||||
|     return new Date(datetime.to_unix() * 1000 + datetime.get_microsecond() / 1000); | ||||
| } | ||||
|  | ||||
| var TodayButton = GObject.registerClass( | ||||
| class TodayButton extends St.Button { | ||||
|     _init(calendar) { | ||||
|         // Having the ability to go to the current date if the user is already | ||||
|         // on the current date can be confusing. So don't make the button reactive | ||||
|         // until the selected date changes. | ||||
|         this.actor = new St.Button({ | ||||
|         super._init({ | ||||
|             style_class: 'datemenu-today-button', | ||||
|             x_align: St.Align.START, | ||||
|             x_expand: true, | ||||
|             can_focus: true, | ||||
|             reactive: false, | ||||
|         }); | ||||
|         this.actor.connect('clicked', () => { | ||||
|             this._calendar.setDate(new Date(), false); | ||||
|         }); | ||||
|  | ||||
|         let hbox = new St.BoxLayout({ vertical: true }); | ||||
|         this.actor.add_actor(hbox); | ||||
|         this.add_actor(hbox); | ||||
|  | ||||
|         this._dayLabel = new St.Label({ style_class: 'day-label', | ||||
|                                         x_align: Clutter.ActorAlign.START }); | ||||
| @@ -52,13 +53,17 @@ var TodayButton = class TodayButton { | ||||
|         hbox.add_actor(this._dateLabel); | ||||
|  | ||||
|         this._calendar = calendar; | ||||
|         this._calendar.connect('selected-date-changed', (calendar, date) => { | ||||
|         this._calendar.connect('selected-date-changed', (_calendar, datetime) => { | ||||
|             // Make the button reactive only if the selected date is not the | ||||
|             // current date. | ||||
|             this.actor.reactive = !_isToday(date); | ||||
|             this.reactive = !_isToday(_gDateTimeToDate(datetime)); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     vfunc_clicked() { | ||||
|         this._calendar.setDate(new Date(), false); | ||||
|     } | ||||
|  | ||||
|     setDate(date) { | ||||
|         this._dayLabel.set_text(date.toLocaleFormat('%A')); | ||||
|  | ||||
| @@ -67,42 +72,38 @@ var TodayButton = class TodayButton { | ||||
|          * "Tue 9:29 AM").  The string itself should become a full date, e.g., | ||||
|          * "February 17 2015". | ||||
|          */ | ||||
|         let dateFormat = Shell.util_translate_time_string (N_("%B %-d %Y")); | ||||
|         let dateFormat = Shell.util_translate_time_string(N_("%B %-d %Y")); | ||||
|         this._dateLabel.set_text(date.toLocaleFormat(dateFormat)); | ||||
|  | ||||
|         /* Translators: This is the accessible name of the date button shown | ||||
|          * below the time in the shell; it should combine the weekday and the | ||||
|          * date, e.g. "Tuesday February 17 2015". | ||||
|          */ | ||||
|         dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y")); | ||||
|         this.actor.accessible_name = date.toLocaleFormat(dateFormat); | ||||
|         dateFormat = Shell.util_translate_time_string(N_("%A %B %e %Y")); | ||||
|         this.accessible_name = date.toLocaleFormat(dateFormat); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var WorldClocksSection = class WorldClocksSection { | ||||
|     constructor() { | ||||
| var WorldClocksSection = GObject.registerClass( | ||||
| class WorldClocksSection extends St.Button { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             style_class: 'world-clocks-button', | ||||
|             can_focus: true, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         this._clock = new GnomeDesktop.WallClock(); | ||||
|         this._clockNotifyId = 0; | ||||
|  | ||||
|         this._locations = []; | ||||
|  | ||||
|         this.actor = new St.Button({ style_class: 'world-clocks-button', | ||||
|                                      x_fill: true, | ||||
|                                      can_focus: true }); | ||||
|         this.actor.connect('clicked', () => { | ||||
|             if (this._clocksApp) | ||||
|                 this._clocksApp.activate(); | ||||
|  | ||||
|             Main.overview.hide(); | ||||
|             Main.panel.closeCalendar(); | ||||
|         }); | ||||
|  | ||||
|         let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         this._grid = new St.Widget({ style_class: 'world-clocks-grid', | ||||
|                                      x_expand: true, | ||||
|                                      layout_manager: layout }); | ||||
|         layout.hookup_style(this._grid); | ||||
|  | ||||
|         this.actor.child = this._grid; | ||||
|         this.child = this._grid; | ||||
|  | ||||
|         this._clocksApp = null; | ||||
|         this._clocksProxy = new ClocksProxy( | ||||
| @@ -114,7 +115,7 @@ var WorldClocksSection = class WorldClocksSection { | ||||
|             Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ | ||||
|             schema_id: 'org.gnome.shell.world-clocks' | ||||
|             schema_id: 'org.gnome.shell.world-clocks', | ||||
|         }); | ||||
|         this._settings.connect('changed', this._clocksChanged.bind(this)); | ||||
|         this._clocksChanged(); | ||||
| @@ -125,9 +126,17 @@ var WorldClocksSection = class WorldClocksSection { | ||||
|         this._sync(); | ||||
|     } | ||||
|  | ||||
|     vfunc_clicked() { | ||||
|         if (this._clocksApp) | ||||
|             this._clocksApp.activate(); | ||||
|  | ||||
|         Main.overview.hide(); | ||||
|         Main.panel.closeCalendar(); | ||||
|     } | ||||
|  | ||||
|     _sync() { | ||||
|         this._clocksApp = this._appSystem.lookup_app('org.gnome.clocks.desktop'); | ||||
|         this.actor.visible = this._clocksApp != null; | ||||
|         this.visible = this._clocksApp != null; | ||||
|     } | ||||
|  | ||||
|     _clocksChanged() { | ||||
| @@ -148,14 +157,14 @@ var WorldClocksSection = class WorldClocksSection { | ||||
|         }); | ||||
|  | ||||
|         let layout = this._grid.layout_manager; | ||||
|         let title = (this._locations.length == 0) | ||||
|         let title = this._locations.length == 0 | ||||
|             ? _("Add world clocks…") | ||||
|             : _("World Clocks"); | ||||
|         let header = new St.Label({ style_class: 'world-clocks-header', | ||||
|                                     x_align: Clutter.ActorAlign.START, | ||||
|                                     text: title }); | ||||
|         layout.attach(header, 0, 0, 2, 1); | ||||
|         this.actor.label_actor = header; | ||||
|         this.label_actor = header; | ||||
|  | ||||
|         let localOffset = GLib.DateTime.new_now_local().get_utc_offset(); | ||||
|  | ||||
| @@ -173,8 +182,8 @@ var WorldClocksSection = class WorldClocksSection { | ||||
|  | ||||
|             let otherOffset = this._getTimeAtLocation(l).get_utc_offset(); | ||||
|             let offset = (otherOffset - localOffset) / GLib.TIME_SPAN_HOUR; | ||||
|             let fmt = (Math.trunc(offset) == offset) ? '%s%.0f' : '%s%.1f'; | ||||
|             let prefix = (offset >= 0) ? '+' : '-'; | ||||
|             let fmt = Math.trunc(offset) == offset ? '%s%.0f' : '%s%.1f'; | ||||
|             let prefix = offset >= 0 ? '+' : '-'; | ||||
|             let tz = new St.Label({ style_class: 'world-clocks-timezone', | ||||
|                                     text: fmt.format(prefix, Math.abs(offset)), | ||||
|                                     x_align: Clutter.ActorAlign.END, | ||||
| @@ -194,9 +203,10 @@ var WorldClocksSection = class WorldClocksSection { | ||||
|         } | ||||
|  | ||||
|         if (this._grid.get_n_children() > 1) { | ||||
|             if (!this._clockNotifyId) | ||||
|             if (!this._clockNotifyId) { | ||||
|                 this._clockNotifyId = | ||||
|                     this._clock.connect('notify::clock', this._updateLabels.bind(this)); | ||||
|             } | ||||
|             this._updateLabels(); | ||||
|         } else { | ||||
|             if (this._clockNotifyId) | ||||
| @@ -236,45 +246,49 @@ var WorldClocksSection = class WorldClocksSection { | ||||
|         this._settings.set_value('locations', | ||||
|             new GLib.Variant('av', this._clocksProxy.Locations)); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var WeatherSection = GObject.registerClass( | ||||
| class WeatherSection extends St.Button { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             style_class: 'weather-button', | ||||
|             can_focus: true, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|  | ||||
| var WeatherSection = class WeatherSection { | ||||
|     constructor() { | ||||
|         this._weatherClient = new Weather.WeatherClient(); | ||||
|  | ||||
|         this.actor = new St.Button({ style_class: 'weather-button', | ||||
|                                      x_fill: true, | ||||
|                                      can_focus: true }); | ||||
|         this.actor.connect('clicked', () => { | ||||
|             this._weatherClient.activateApp(); | ||||
|  | ||||
|             Main.overview.hide(); | ||||
|             Main.panel.closeCalendar(); | ||||
|         }); | ||||
|         this.actor.connect('notify::mapped', () => { | ||||
|             if (this.actor.mapped) | ||||
|                 this._weatherClient.update(); | ||||
|         let box = new St.BoxLayout({ | ||||
|             style_class: 'weather-box', | ||||
|             vertical: true, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|  | ||||
|         let box = new St.BoxLayout({ style_class: 'weather-box', | ||||
|                                      vertical: true }); | ||||
|         this.child = box; | ||||
|  | ||||
|         this.actor.child = box; | ||||
|  | ||||
|         let titleBox = new St.BoxLayout(); | ||||
|         titleBox.add_child(new St.Label({ style_class: 'weather-header', | ||||
|                                           x_align: Clutter.ActorAlign.START, | ||||
|                                           x_expand: true, | ||||
|                                           text: _("Weather") })); | ||||
|         let titleBox = new St.BoxLayout({ style_class: 'weather-header-box' }); | ||||
|         titleBox.add_child(new St.Label({ | ||||
|             style_class: 'weather-header', | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|             x_expand: true, | ||||
|             y_align: Clutter.ActorAlign.END, | ||||
|             text: _('Weather'), | ||||
|         })); | ||||
|         box.add_child(titleBox); | ||||
|  | ||||
|         this._titleLocation = new St.Label({ style_class: 'weather-header location', | ||||
|                                              x_align: Clutter.ActorAlign.END }); | ||||
|         this._titleLocation = new St.Label({ | ||||
|             style_class: 'weather-header location', | ||||
|             x_align: Clutter.ActorAlign.END, | ||||
|             y_align: Clutter.ActorAlign.END, | ||||
|         }); | ||||
|         titleBox.add_child(this._titleLocation); | ||||
|  | ||||
|         let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         this._forecastGrid = new St.Widget({ style_class: 'weather-grid', | ||||
|                                              layout_manager: layout }); | ||||
|         this._forecastGrid = new St.Widget({ | ||||
|             style_class: 'weather-grid', | ||||
|             layout_manager: layout, | ||||
|         }); | ||||
|         layout.hookup_style(this._forecastGrid); | ||||
|         box.add_child(this._forecastGrid); | ||||
|  | ||||
| @@ -282,26 +296,40 @@ var WeatherSection = class WeatherSection { | ||||
|         this._sync(); | ||||
|     } | ||||
|  | ||||
|     vfunc_map() { | ||||
|         this._weatherClient.update(); | ||||
|         super.vfunc_map(); | ||||
|     } | ||||
|  | ||||
|     vfunc_clicked() { | ||||
|         this._weatherClient.activateApp(); | ||||
|  | ||||
|         Main.overview.hide(); | ||||
|         Main.panel.closeCalendar(); | ||||
|     } | ||||
|  | ||||
|     _getInfos() { | ||||
|         let info = this._weatherClient.info; | ||||
|         let forecasts = info.get_forecast_list(); | ||||
|         let forecasts = this._weatherClient.info.get_forecast_list(); | ||||
|  | ||||
|         let current = info; | ||||
|         let infos = [info]; | ||||
|         let now = GLib.DateTime.new_now_local(); | ||||
|         let current = GLib.DateTime.new_from_unix_local(0); | ||||
|         let infos = []; | ||||
|         for (let i = 0; i < forecasts.length; i++) { | ||||
|             let [ok_, timestamp] = forecasts[i].get_value_update(); | ||||
|             let datetime = new Date(timestamp * 1000); | ||||
|             if (!_isToday(datetime)) | ||||
|                 continue; // Ignore forecasts from other days | ||||
|             const [valid, timestamp] = forecasts[i].get_value_update(); | ||||
|             if (!valid || timestamp === 0) | ||||
|                 continue;  // 0 means 'never updated' | ||||
|  | ||||
|             [ok_, timestamp] = current.get_value_update(); | ||||
|             let currenttime = new Date(timestamp * 1000); | ||||
|             if (currenttime.getHours() == datetime.getHours()) | ||||
|             const datetime = GLib.DateTime.new_from_unix_local(timestamp); | ||||
|             if (now.difference(datetime) > 0) | ||||
|                 continue; // Ignore earlier forecasts | ||||
|  | ||||
|             if (datetime.difference(current) < GLib.TIME_SPAN_HOUR) | ||||
|                 continue; // Enforce a minimum interval of 1h | ||||
|  | ||||
|             current = forecasts[i]; | ||||
|             if (infos.push(current) == MAX_FORECASTS) | ||||
|             if (infos.push(forecasts[i]) == MAX_FORECASTS) | ||||
|                 break; // Use a maximum of five forecasts | ||||
|  | ||||
|             current = datetime; | ||||
|         } | ||||
|         return infos; | ||||
|     } | ||||
| @@ -315,21 +343,31 @@ var WeatherSection = class WeatherSection { | ||||
|  | ||||
|         let col = 0; | ||||
|         infos.forEach(fc => { | ||||
|             let [ok_, timestamp] = fc.get_value_update(); | ||||
|             const [valid_, timestamp] = fc.get_value_update(); | ||||
|             let timeStr = Util.formatTime(new Date(timestamp * 1000), { | ||||
|                 timeOnly: true | ||||
|                 timeOnly: true, | ||||
|                 ampm: false, | ||||
|             }); | ||||
|  | ||||
|             let icon = new St.Icon({ style_class: 'weather-forecast-icon', | ||||
|                                      icon_name: fc.get_symbolic_icon_name(), | ||||
|                                      x_align: Clutter.ActorAlign.CENTER, | ||||
|                                      x_expand: true }); | ||||
|             let temp = new St.Label({ style_class: 'weather-forecast-temp', | ||||
|                                       text: fc.get_temp_summary(), | ||||
|                                       x_align: Clutter.ActorAlign.CENTER }); | ||||
|             let time = new St.Label({ style_class: 'weather-forecast-time', | ||||
|                                       text: timeStr, | ||||
|                                       x_align: Clutter.ActorAlign.CENTER }); | ||||
|             let icon = new St.Icon({ | ||||
|                 style_class: 'weather-forecast-icon', | ||||
|                 icon_name: fc.get_symbolic_icon_name(), | ||||
|                 x_align: Clutter.ActorAlign.CENTER, | ||||
|                 x_expand: true, | ||||
|             }); | ||||
|             let temp = new St.Label({ | ||||
|                 style_class: 'weather-forecast-temp', | ||||
|                 text: fc.get_temp_summary(), | ||||
|                 x_align: Clutter.ActorAlign.CENTER, | ||||
|             }); | ||||
|             let time = new St.Label({ | ||||
|                 style_class: 'weather-forecast-time', | ||||
|                 text: timeStr, | ||||
|                 x_align: Clutter.ActorAlign.CENTER, | ||||
|             }); | ||||
|  | ||||
|             temp.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|             time.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|  | ||||
|             layout.attach(icon, col, 0, 1, 1); | ||||
|             layout.attach(temp, col, 1, 1, 1); | ||||
| @@ -353,7 +391,12 @@ var WeatherSection = class WeatherSection { | ||||
|         } | ||||
|  | ||||
|         let info = this._weatherClient.info; | ||||
|         this._titleLocation.text = info.get_location().get_name(); | ||||
|         let loc = info.get_location(); | ||||
|         if (loc.get_level() !== GWeather.LocationLevel.CITY && loc.has_coords()) { | ||||
|             let world = GWeather.Location.get_world(); | ||||
|             loc = world.find_nearest_city(...loc.get_coords()); | ||||
|         } | ||||
|         this._titleLocation.text = loc.get_name(); | ||||
|  | ||||
|         if (this._weatherClient.loading) { | ||||
|             this._setStatusLabel(_("Loading…")); | ||||
| @@ -372,23 +415,27 @@ var WeatherSection = class WeatherSection { | ||||
|     } | ||||
|  | ||||
|     _sync() { | ||||
|         this.actor.visible = this._weatherClient.available; | ||||
|         this.visible = this._weatherClient.available; | ||||
|  | ||||
|         if (!this.actor.visible) | ||||
|         if (!this.visible) | ||||
|             return; | ||||
|  | ||||
|         this._titleLocation.visible = this._weatherClient.hasLocation; | ||||
|  | ||||
|         this._updateForecasts(); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var MessagesIndicator = class MessagesIndicator { | ||||
|     constructor() { | ||||
|         this.actor = new St.Icon({ icon_name: 'message-indicator-symbolic', | ||||
|                                    icon_size: 16, | ||||
|                                    visible: false, y_expand: true, | ||||
|                                    y_align: Clutter.ActorAlign.CENTER }); | ||||
| var MessagesIndicator = GObject.registerClass( | ||||
| class MessagesIndicator extends St.Icon { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             icon_name: 'message-indicator-symbolic', | ||||
|             icon_size: 16, | ||||
|             visible: false, | ||||
|             y_expand: true, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|  | ||||
|         this._sources = []; | ||||
|  | ||||
| @@ -401,7 +448,7 @@ var MessagesIndicator = class MessagesIndicator { | ||||
|     } | ||||
|  | ||||
|     _onSourceAdded(tray, source) { | ||||
|         source.connect('count-updated', this._updateCount.bind(this)); | ||||
|         source.connect('notify::count', this._updateCount.bind(this)); | ||||
|         this._sources.push(source); | ||||
|         this._updateCount(); | ||||
|     } | ||||
| @@ -416,9 +463,9 @@ var MessagesIndicator = class MessagesIndicator { | ||||
|         this._sources.forEach(source => (count += source.unseenCount)); | ||||
|         count -= Main.messageTray.queueCount; | ||||
|  | ||||
|         this.actor.visible = (count > 0); | ||||
|         this.visible = count > 0; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var IndicatorPad = GObject.registerClass( | ||||
| class IndicatorPad extends St.Widget { | ||||
| @@ -511,13 +558,13 @@ class DateMenuButton extends PanelMenu.Button { | ||||
|         this._indicator = new MessagesIndicator(); | ||||
|  | ||||
|         let box = new St.BoxLayout(); | ||||
|         box.add_actor(new IndicatorPad(this._indicator.actor)); | ||||
|         box.add_actor(new IndicatorPad(this._indicator)); | ||||
|         box.add_actor(this._clockDisplay); | ||||
|         box.add_actor(this._indicator.actor); | ||||
|         box.add_actor(this._indicator); | ||||
|  | ||||
|         this.label_actor = this._clockDisplay; | ||||
|         this.add_actor(box); | ||||
|         this.add_style_class_name ('clock-display'); | ||||
|         this.add_style_class_name('clock-display'); | ||||
|  | ||||
|         let layout = new FreezableBinLayout(); | ||||
|         let bin = new St.Widget({ layout_manager: layout }); | ||||
| @@ -529,11 +576,11 @@ class DateMenuButton extends PanelMenu.Button { | ||||
|         bin.add_actor(hbox); | ||||
|  | ||||
|         this._calendar = new Calendar.Calendar(); | ||||
|         this._calendar.connect('selected-date-changed', | ||||
|                                (calendar, date) => { | ||||
|                                    layout.frozen = !_isToday(date); | ||||
|                                    this._messageList.setDate(date); | ||||
|                                }); | ||||
|         this._calendar.connect('selected-date-changed', (_calendar, datetime) => { | ||||
|             let date = _gDateTimeToDate(datetime); | ||||
|             layout.frozen = !_isToday(date); | ||||
|             this._messageList.setDate(date); | ||||
|         }); | ||||
|  | ||||
|         this.menu.connect('open-state-changed', (menu, isOpen) => { | ||||
|             // Whenever the menu is opened, select today | ||||
| @@ -547,35 +594,36 @@ class DateMenuButton extends PanelMenu.Button { | ||||
|  | ||||
|         // Fill up the first column | ||||
|         this._messageList = new Calendar.CalendarMessageList(); | ||||
|         hbox.add(this._messageList.actor, { expand: true, y_fill: false, y_align: St.Align.START }); | ||||
|         hbox.add_child(this._messageList); | ||||
|  | ||||
|         // Fill up the second column | ||||
|         let boxLayout = new CalendarColumnLayout(this._calendar.actor); | ||||
|         let boxLayout = new CalendarColumnLayout(this._calendar); | ||||
|         vbox = new St.Widget({ style_class: 'datemenu-calendar-column', | ||||
|                                layout_manager: boxLayout }); | ||||
|         boxLayout.hookup_style(vbox); | ||||
|         hbox.add(vbox); | ||||
|  | ||||
|         this._date = new TodayButton(this._calendar); | ||||
|         vbox.add_actor(this._date.actor); | ||||
|         vbox.add_actor(this._date); | ||||
|  | ||||
|         vbox.add_actor(this._calendar.actor); | ||||
|         vbox.add_actor(this._calendar); | ||||
|  | ||||
|         this._displaysSection = new St.ScrollView({ style_class: 'datemenu-displays-section vfade', | ||||
|                                                     x_expand: true, x_fill: true, | ||||
|                                                     x_expand: true, | ||||
|                                                     overlay_scrollbars: true }); | ||||
|         this._displaysSection.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); | ||||
|         vbox.add_actor(this._displaysSection); | ||||
|  | ||||
|         let displaysBox = new St.BoxLayout({ vertical: true, | ||||
|                                              x_expand: true, | ||||
|                                              style_class: 'datemenu-displays-box' }); | ||||
|         this._displaysSection.add_actor(displaysBox); | ||||
|  | ||||
|         this._clocksItem = new WorldClocksSection(); | ||||
|         displaysBox.add(this._clocksItem.actor, { x_fill: true }); | ||||
|         displaysBox.add_child(this._clocksItem); | ||||
|  | ||||
|         this._weatherItem = new WeatherSection(); | ||||
|         displaysBox.add(this._weatherItem.actor, { x_fill: true }); | ||||
|         displaysBox.add_child(this._weatherItem); | ||||
|  | ||||
|         // Done with hbox for calendar and event list | ||||
|  | ||||
| @@ -613,11 +661,11 @@ class DateMenuButton extends PanelMenu.Button { | ||||
|     _sessionUpdated() { | ||||
|         let eventSource; | ||||
|         let showEvents = Main.sessionMode.showCalendarEvents; | ||||
|         if (showEvents) { | ||||
|         if (showEvents) | ||||
|             eventSource = this._getEventSource(); | ||||
|         } else { | ||||
|         else | ||||
|             eventSource = new Calendar.EmptyEventSource(); | ||||
|         } | ||||
|  | ||||
|         this._setEventSource(eventSource); | ||||
|  | ||||
|         // Displays are not actually expected to launch Settings when activated | ||||
|   | ||||
| @@ -36,19 +36,15 @@ class Dialog extends St.Widget { | ||||
|         this._dialog.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH; | ||||
|         this._dialog.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); | ||||
|  | ||||
|         this.contentLayout = new St.BoxLayout({ vertical: true, | ||||
|                                                 style_class: "modal-dialog-content-box" }); | ||||
|         this._dialog.add(this.contentLayout, | ||||
|                          { expand: true, | ||||
|                            x_fill: true, | ||||
|                            y_fill: true, | ||||
|                            x_align: St.Align.MIDDLE, | ||||
|                            y_align: St.Align.START }); | ||||
|         this.contentLayout = new St.BoxLayout({ | ||||
|             vertical: true, | ||||
|             style_class: 'modal-dialog-content-box', | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this._dialog.add_child(this.contentLayout); | ||||
|  | ||||
|         this.buttonLayout = new St.Widget ({ layout_manager: new Clutter.BoxLayout({ homogeneous: true }) }); | ||||
|         this._dialog.add(this.buttonLayout, | ||||
|                          { x_align: St.Align.MIDDLE, | ||||
|                            y_align: St.Align.START }); | ||||
|         this.buttonLayout = new St.Widget({ layout_manager: new Clutter.BoxLayout({ homogeneous: true }) }); | ||||
|         this._dialog.add_child(this.buttonLayout); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
| @@ -100,7 +96,7 @@ class Dialog extends St.Widget { | ||||
|     } | ||||
|  | ||||
|     addContent(actor) { | ||||
|         this.contentLayout.add (actor, { expand: true }); | ||||
|         this.contentLayout.add(actor, { expand: true }); | ||||
|     } | ||||
|  | ||||
|     addButton(buttonInfo) { | ||||
| @@ -121,7 +117,7 @@ class Dialog extends St.Widget { | ||||
|                                      can_focus: true, | ||||
|                                      x_expand: true, | ||||
|                                      y_expand: true, | ||||
|                                      label: label }); | ||||
|                                      label }); | ||||
|         button.connect('clicked', action); | ||||
|  | ||||
|         buttonInfo['button'] = button; | ||||
| @@ -163,8 +159,8 @@ var MessageDialogContent = GObject.registerClass({ | ||||
|         'body': GObject.ParamSpec.string('body', 'body', 'body', | ||||
|                                          GObject.ParamFlags.READWRITE | | ||||
|                                          GObject.ParamFlags.CONSTRUCT, | ||||
|                                          null) | ||||
|     } | ||||
|                                          null), | ||||
|     }, | ||||
| }, class MessageDialogContent extends St.BoxLayout { | ||||
|     _init(params) { | ||||
|         this._icon = new St.Icon({ y_align: Clutter.ActorAlign.START }); | ||||
| @@ -215,7 +211,7 @@ var MessageDialogContent = GObject.registerClass({ | ||||
|     set icon(icon) { | ||||
|         this._icon.set({ | ||||
|             gicon: icon, | ||||
|             visible: icon != null | ||||
|             visible: icon != null, | ||||
|         }); | ||||
|         this.notify('icon'); | ||||
|     } | ||||
| @@ -235,7 +231,7 @@ var MessageDialogContent = GObject.registerClass({ | ||||
|     _setLabel(label, prop, value) { | ||||
|         label.set({ | ||||
|             text: value || '', | ||||
|             visible: value != null | ||||
|             visible: value != null, | ||||
|         }); | ||||
|         this.notify(prop); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										75
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							| @@ -18,7 +18,7 @@ var DragMotionResult = { | ||||
|     NO_DROP:   0, | ||||
|     COPY_DROP: 1, | ||||
|     MOVE_DROP: 2, | ||||
|     CONTINUE:  3 | ||||
|     CONTINUE:  3, | ||||
| }; | ||||
|  | ||||
| var DragState = { | ||||
| @@ -30,13 +30,13 @@ var DragState = { | ||||
| var DRAG_CURSOR_MAP = { | ||||
|     0: Meta.Cursor.DND_UNSUPPORTED_TARGET, | ||||
|     1: Meta.Cursor.DND_COPY, | ||||
|     2: Meta.Cursor.DND_MOVE | ||||
|     2: Meta.Cursor.DND_MOVE, | ||||
| }; | ||||
|  | ||||
| var DragDropResult = { | ||||
|     FAILURE:  0, | ||||
|     SUCCESS:  1, | ||||
|     CONTINUE: 2 | ||||
|     CONTINUE: 2, | ||||
| }; | ||||
| var dragMonitors = []; | ||||
|  | ||||
| @@ -61,11 +61,12 @@ function addDragMonitor(monitor) { | ||||
| } | ||||
|  | ||||
| function removeDragMonitor(monitor) { | ||||
|     for (let i = 0; i < dragMonitors.length; i++) | ||||
|     for (let i = 0; i < dragMonitors.length; i++) { | ||||
|         if (dragMonitors[i] == monitor) { | ||||
|             dragMonitors.splice(i, 1); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| var _Draggable = class _Draggable { | ||||
| @@ -150,12 +151,12 @@ var _Draggable = class _Draggable { | ||||
|         if (touchSequence) | ||||
|             pointer.sequence_grab(touchSequence, actor); | ||||
|         else if (pointer) | ||||
|             pointer.grab (actor); | ||||
|             pointer.grab(actor); | ||||
|  | ||||
|         this._grabbedDevice = pointer; | ||||
|         this._touchSequence = touchSequence; | ||||
|  | ||||
|         this._capturedEventId = global.stage.connect('captured-event', (actor, event) => { | ||||
|         this._capturedEventId = global.stage.connect('captured-event', (o, event) => { | ||||
|             let device = event.get_device(); | ||||
|             if (device != this._grabbedDevice && | ||||
|                 device.get_device_type() != Clutter.InputDeviceType.KEYBOARD_DEVICE) | ||||
| @@ -171,7 +172,7 @@ var _Draggable = class _Draggable { | ||||
|         } | ||||
|  | ||||
|         if (this._touchSequence) | ||||
|             this._grabbedDevice.sequence_ungrab (this._touchSequence); | ||||
|             this._grabbedDevice.sequence_ungrab(this._touchSequence); | ||||
|         else | ||||
|             this._grabbedDevice.ungrab(); | ||||
|  | ||||
| @@ -212,9 +213,9 @@ var _Draggable = class _Draggable { | ||||
|  | ||||
|     _eventIsRelease(event) { | ||||
|         if (event.type() == Clutter.EventType.BUTTON_RELEASE) { | ||||
|             let buttonMask = (Clutter.ModifierType.BUTTON1_MASK | | ||||
|             let buttonMask = Clutter.ModifierType.BUTTON1_MASK | | ||||
|                               Clutter.ModifierType.BUTTON2_MASK | | ||||
|                               Clutter.ModifierType.BUTTON3_MASK); | ||||
|                               Clutter.ModifierType.BUTTON3_MASK; | ||||
|             /* We only obey the last button release from the device, | ||||
|              * other buttons may get pressed/released during the DnD op. | ||||
|              */ | ||||
| @@ -258,16 +259,16 @@ var _Draggable = class _Draggable { | ||||
|         } else if (event.type() == Clutter.EventType.MOTION || | ||||
|                    (event.type() == Clutter.EventType.TOUCH_UPDATE && | ||||
|                     global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) { | ||||
|             if (this._dragActor && this._dragState == DragState.DRAGGING) { | ||||
|             if (this._dragActor && this._dragState == DragState.DRAGGING) | ||||
|                 return this._updateDragPosition(event); | ||||
|             } else if (this._dragActor == null && this._dragState != DragState.CANCELLED) { | ||||
|             else if (this._dragActor == null && this._dragState != DragState.CANCELLED) | ||||
|                 return this._maybeStartDrag(event); | ||||
|             } | ||||
|  | ||||
|         // We intercept KEY_PRESS event so that we can process Esc key press to cancel | ||||
|         // dragging and ignore all other key presses. | ||||
|         } else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragState == DragState.DRAGGING) { | ||||
|             let symbol = event.get_key_symbol(); | ||||
|             if (symbol == Clutter.Escape) { | ||||
|             if (symbol == Clutter.KEY_Escape) { | ||||
|                 this._cancelDrag(event.get_time()); | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
| @@ -291,9 +292,11 @@ var _Draggable = class _Draggable { | ||||
|  | ||||
|     /** | ||||
|      * startDrag: | ||||
|      * @stageX: X coordinate of event | ||||
|      * @stageY: Y coordinate of event | ||||
|      * @time: Event timestamp | ||||
|      * @param {number} stageX: X coordinate of event | ||||
|      * @param {number} stageY: Y coordinate of event | ||||
|      * @param {number} time: Event timestamp | ||||
|      * @param {Clutter.EventSequence=} sequence: Event sequence | ||||
|      * @param {Clutter.InputDevice=} device: device that originated the event | ||||
|      * | ||||
|      * Directly initiate a drag and drop operation from the given actor. | ||||
|      * This function is useful to call if you've specified manualMode | ||||
| @@ -338,7 +341,7 @@ var _Draggable = class _Draggable { | ||||
|         if (this.actor._delegate && this.actor._delegate.getDragActor) { | ||||
|             this._dragActor = this.actor._delegate.getDragActor(); | ||||
|             Main.uiGroup.add_child(this._dragActor); | ||||
|             this._dragActor.raise_top(); | ||||
|             Main.uiGroup.set_child_above_sibling(this._dragActor, null); | ||||
|             Shell.util_set_hidden_from_pick(this._dragActor, true); | ||||
|  | ||||
|             // Drag actor does not always have to be the same as actor. For example drag actor | ||||
| @@ -388,7 +391,7 @@ var _Draggable = class _Draggable { | ||||
|  | ||||
|             this._dragOrigParent.remove_actor(this._dragActor); | ||||
|             Main.uiGroup.add_child(this._dragActor); | ||||
|             this._dragActor.raise_top(); | ||||
|             Main.uiGroup.set_child_above_sibling(this._dragActor, null); | ||||
|             Shell.util_set_hidden_from_pick(this._dragActor, true); | ||||
|         } | ||||
|  | ||||
| @@ -427,7 +430,7 @@ var _Draggable = class _Draggable { | ||||
|                     scale_x: scale * origScale, | ||||
|                     scale_y: scale * origScale, | ||||
|                     duration: SCALE_ANIMATION_TIME, | ||||
|                     mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                     mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 }); | ||||
|  | ||||
|                 this._dragActor.get_transition('scale-x').connect('new-frame', () => { | ||||
| @@ -472,7 +475,7 @@ var _Draggable = class _Draggable { | ||||
|             y: this._dragY, | ||||
|             dragActor: this._dragActor, | ||||
|             source: this.actor._delegate, | ||||
|             targetActor: target | ||||
|             targetActor: target, | ||||
|         }; | ||||
|  | ||||
|         let targetActorDestroyHandlerId; | ||||
| @@ -551,11 +554,11 @@ var _Draggable = class _Draggable { | ||||
|         let dropEvent = { | ||||
|             dropActor: this._dragActor, | ||||
|             targetActor: target, | ||||
|             clutterEvent: event | ||||
|             clutterEvent: event, | ||||
|         }; | ||||
|         for (let i = 0; i < dragMonitors.length; i++) { | ||||
|             let dropFunc = dragMonitors[i].dragDrop; | ||||
|             if (dropFunc) | ||||
|             if (dropFunc) { | ||||
|                 switch (dropFunc(dropEvent)) { | ||||
|                 case DragDropResult.FAILURE: | ||||
|                 case DragDropResult.SUCCESS: | ||||
| @@ -563,6 +566,7 @@ var _Draggable = class _Draggable { | ||||
|                 case DragDropResult.CONTINUE: | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // At this point it is too late to cancel a drag by destroying | ||||
| @@ -573,11 +577,15 @@ var _Draggable = class _Draggable { | ||||
|         while (target) { | ||||
|             if (target._delegate && target._delegate.acceptDrop) { | ||||
|                 let [r_, targX, targY] = target.transform_stage_point(dropX, dropY); | ||||
|                 if (target._delegate.acceptDrop(this.actor._delegate, | ||||
|                                                 this._dragActor, | ||||
|                                                 targX, | ||||
|                                                 targY, | ||||
|                                                 event.get_time())) { | ||||
|                 let accepted = false; | ||||
|                 try { | ||||
|                     accepted = target._delegate.acceptDrop(this.actor._delegate, | ||||
|                         this._dragActor, targX, targY, event.get_time()); | ||||
|                 } catch (e) { | ||||
|                     // On error, skip this target | ||||
|                     logError(e, "Skipping drag target"); | ||||
|                 } | ||||
|                 if (accepted) { | ||||
|                     // If it accepted the drop without taking the actor, | ||||
|                     // handle it ourselves. | ||||
|                     if (this._dragActor && this._dragActor.get_parent() == Main.uiGroup) { | ||||
| @@ -638,7 +646,7 @@ var _Draggable = class _Draggable { | ||||
|  | ||||
|     _cancelDrag(eventTime) { | ||||
|         this.emit('drag-cancelled', eventTime); | ||||
|         let wasCancelled = (this._dragState == DragState.CANCELLED); | ||||
|         let wasCancelled = this._dragState == DragState.CANCELLED; | ||||
|         this._dragState = DragState.CANCELLED; | ||||
|  | ||||
|         if (this._actorDestroyed || wasCancelled) { | ||||
| @@ -659,7 +667,7 @@ var _Draggable = class _Draggable { | ||||
|             y: snapBackY, | ||||
|             scale_x: snapBackScale, | ||||
|             scale_y: snapBackScale, | ||||
|             duration: SNAP_BACK_ANIMATION_TIME | ||||
|             duration: SNAP_BACK_ANIMATION_TIME, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -673,7 +681,7 @@ var _Draggable = class _Draggable { | ||||
|         this._dragActor.opacity = 0; | ||||
|  | ||||
|         this._animateDragEnd(eventTime, { | ||||
|             duration: REVERT_ANIMATION_TIME | ||||
|             duration: REVERT_ANIMATION_TIME, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -686,7 +694,7 @@ var _Draggable = class _Draggable { | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => { | ||||
|                 this._onAnimationComplete(this._dragActor, eventTime); | ||||
|             } | ||||
|             }, | ||||
|         })); | ||||
|     } | ||||
|  | ||||
| @@ -740,8 +748,9 @@ Signals.addSignalMethods(_Draggable.prototype); | ||||
|  | ||||
| /** | ||||
|  * makeDraggable: | ||||
|  * @actor: Source actor | ||||
|  * @params: (optional) Additional parameters | ||||
|  * @param {Clutter.Actor} actor: Source actor | ||||
|  * @param {Object=} params: Additional parameters | ||||
|  * @returns {Object} a new Draggable | ||||
|  * | ||||
|  * Create an object which controls drag and drop for the given actor. | ||||
|  * | ||||
|   | ||||
| @@ -37,17 +37,17 @@ var EdgeDragAction = GObject.registerClass({ | ||||
|         let [x, y] = this.get_press_coords(0); | ||||
|         let monitorRect = this._getMonitorRect(x, y); | ||||
|  | ||||
|         return ((this._side == St.Side.LEFT && x < monitorRect.x + EDGE_THRESHOLD) || | ||||
|         return (this._side == St.Side.LEFT && x < monitorRect.x + EDGE_THRESHOLD) || | ||||
|                 (this._side == St.Side.RIGHT && x > monitorRect.x + monitorRect.width - EDGE_THRESHOLD) || | ||||
|                 (this._side == St.Side.TOP && y < monitorRect.y + EDGE_THRESHOLD) || | ||||
|                 (this._side == St.Side.BOTTOM && y > monitorRect.y + monitorRect.height - EDGE_THRESHOLD)); | ||||
|                 (this._side == St.Side.BOTTOM && y > monitorRect.y + monitorRect.height - EDGE_THRESHOLD); | ||||
|     } | ||||
|  | ||||
|     vfunc_gesture_progress(_actor) { | ||||
|         let [startX, startY] = this.get_press_coords(0); | ||||
|         let [x, y] = this.get_motion_coords(0); | ||||
|         let offsetX = Math.abs (x - startX); | ||||
|         let offsetY = Math.abs (y - startY); | ||||
|         let offsetX = Math.abs(x - startX); | ||||
|         let offsetY = Math.abs(y - startY); | ||||
|  | ||||
|         if (offsetX < EDGE_THRESHOLD && offsetY < EDGE_THRESHOLD) | ||||
|             return true; | ||||
|   | ||||
| @@ -128,7 +128,7 @@ const DialogType = { | ||||
|     SHUTDOWN: 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */, | ||||
|     RESTART: 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */, | ||||
|     UPDATE_RESTART: 3, | ||||
|     UPGRADE_RESTART: 4 | ||||
|     UPGRADE_RESTART: 4, | ||||
| }; | ||||
|  | ||||
| const DialogContent = { | ||||
| @@ -136,7 +136,7 @@ const DialogContent = { | ||||
|     1 /* DialogType.SHUTDOWN */: shutdownDialogContent, | ||||
|     2 /* DialogType.RESTART */: restartDialogContent, | ||||
|     3 /* DialogType.UPDATE_RESTART */: restartUpdateDialogContent, | ||||
|     4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent | ||||
|     4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent, | ||||
| }; | ||||
|  | ||||
| var MAX_USERS_IN_SESSION_DIALOG = 5; | ||||
| @@ -207,10 +207,10 @@ function _setCheckBoxLabel(checkBox, text) { | ||||
|  | ||||
|     if (text) { | ||||
|         label.set_text(text); | ||||
|         checkBox.actor.show(); | ||||
|         checkBox.show(); | ||||
|     } else { | ||||
|         label.set_text(''); | ||||
|         checkBox.actor.hide(); | ||||
|         checkBox.hide(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -218,7 +218,7 @@ function init() { | ||||
|     // This always returns the same singleton object | ||||
|     // By instantiating it initially, we register the | ||||
|     // bus object, etc. | ||||
|     (new EndSessionDialog()); | ||||
|     new EndSessionDialog(); | ||||
| } | ||||
|  | ||||
| var EndSessionDialog = GObject.registerClass( | ||||
| @@ -263,42 +263,39 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|         this._userLoadedId = this._user.connect('notify::is_loaded', this._sync.bind(this)); | ||||
|         this._userChangedId = this._user.connect('changed', this._sync.bind(this)); | ||||
|  | ||||
|         let mainContentLayout = new St.BoxLayout({ vertical: false }); | ||||
|         this.contentLayout.add(mainContentLayout, | ||||
|                                { x_fill: true, | ||||
|                                  y_fill: false }); | ||||
|         let mainContentLayout = new St.BoxLayout({ | ||||
|             vertical: false, | ||||
|             x_expand: true, | ||||
|             y_expand: false, | ||||
|         }); | ||||
|         this.contentLayout.add_child(mainContentLayout); | ||||
|  | ||||
|         this._iconBin = new St.Bin(); | ||||
|         mainContentLayout.add(this._iconBin, | ||||
|                               { x_fill: true, | ||||
|                                 y_fill: false, | ||||
|                                 x_align: St.Align.END, | ||||
|                                 y_align: St.Align.START }); | ||||
|         this._iconBin = new St.Bin({ | ||||
|             x_expand: true, | ||||
|             x_align: Clutter.ActorAlign.END, | ||||
|         }); | ||||
|         mainContentLayout.add_child(this._iconBin); | ||||
|  | ||||
|         let messageLayout = new St.BoxLayout({ vertical: true, | ||||
|                                                style_class: 'end-session-dialog-layout' }); | ||||
|         mainContentLayout.add(messageLayout, | ||||
|                               { y_align: St.Align.START }); | ||||
|         mainContentLayout.add_child(messageLayout); | ||||
|  | ||||
|         this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' }); | ||||
|  | ||||
|         messageLayout.add(this._subjectLabel, | ||||
|                           { x_fill: false, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.START, | ||||
|                             y_align: St.Align.START }); | ||||
|         messageLayout.add_child(this._subjectLabel); | ||||
|  | ||||
|         this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' }); | ||||
|         this._descriptionLabel = new St.Label({ | ||||
|             style_class: 'end-session-dialog-description', | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._descriptionLabel.clutter_text.line_wrap = true; | ||||
|  | ||||
|         messageLayout.add(this._descriptionLabel, | ||||
|                           { y_fill: true, | ||||
|                             y_align: St.Align.START }); | ||||
|         messageLayout.add_child(this._descriptionLabel); | ||||
|  | ||||
|         this._checkBox = new CheckBox.CheckBox(); | ||||
|         this._checkBox.actor.connect('clicked', this._sync.bind(this)); | ||||
|         messageLayout.add(this._checkBox.actor); | ||||
|         this._checkBox.connect('clicked', this._sync.bind(this)); | ||||
|         messageLayout.add(this._checkBox); | ||||
|  | ||||
|         this._batteryWarning = new St.Label({ style_class: 'end-session-dialog-warning', | ||||
|                                               text: _("Running on battery power: please plug in before installing updates.") }); | ||||
| @@ -306,11 +303,13 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|         this._batteryWarning.clutter_text.line_wrap = true; | ||||
|         messageLayout.add(this._batteryWarning); | ||||
|  | ||||
|         this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' }); | ||||
|         this._scrollView = new St.ScrollView({ | ||||
|             style_class: 'end-session-dialog-list', | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); | ||||
|         this.contentLayout.add(this._scrollView, | ||||
|                                { x_fill: true, | ||||
|                                  y_fill: true }); | ||||
|         this.contentLayout.add_child(this._scrollView); | ||||
|         this._scrollView.hide(); | ||||
|  | ||||
|         this._inhibitorSection = new St.BoxLayout({ vertical: true, | ||||
| @@ -367,7 +366,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|     } | ||||
|  | ||||
|     _sync() { | ||||
|         let open = (this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED); | ||||
|         let open = this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED; | ||||
|         if (!open) | ||||
|             return; | ||||
|  | ||||
| @@ -376,12 +375,12 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|         let subject = dialogContent.subject; | ||||
|  | ||||
|         // Use different title when we are installing updates | ||||
|         if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked) | ||||
|         if (dialogContent.subjectWithUpdates && this._checkBox.checked) | ||||
|             subject = dialogContent.subjectWithUpdates; | ||||
|  | ||||
|         if (dialogContent.showBatteryWarning) { | ||||
|             // Warn when running on battery power | ||||
|             if (this._powerProxy.OnBattery && this._checkBox.actor.checked) | ||||
|             if (this._powerProxy.OnBattery && this._checkBox.checked) | ||||
|                 this._batteryWarning.opacity = 255; | ||||
|             else | ||||
|                 this._batteryWarning.opacity = 0; | ||||
| @@ -429,7 +428,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|             let avatarWidget = new UserWidget.Avatar(this._user, | ||||
|                                                      { iconSize: _DIALOG_ICON_SIZE, | ||||
|                                                        styleClass: dialogContent.iconStyleClass }); | ||||
|             this._iconBin.child = avatarWidget.actor; | ||||
|             this._iconBin.child = avatarWidget; | ||||
|             avatarWidget.update(); | ||||
|         } | ||||
|  | ||||
| @@ -444,7 +443,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|         let dialogContent = DialogContent[this._type]; | ||||
|         let buttons = [{ action: this.cancel.bind(this), | ||||
|                          label: _("Cancel"), | ||||
|                          key: Clutter.Escape }]; | ||||
|                          key: Clutter.KEY_Escape }]; | ||||
|  | ||||
|         for (let i = 0; i < dialogContent.confirmButtons.length; i++) { | ||||
|             let signal = dialogContent.confirmButtons[i].signal; | ||||
| @@ -457,7 +456,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|                         this._confirm(signal); | ||||
|                     }); | ||||
|                 }, | ||||
|                 label: label, | ||||
|                 label, | ||||
|             }); | ||||
|         } | ||||
|  | ||||
| @@ -485,13 +484,13 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|         }; | ||||
|  | ||||
|         // Offline update not available; just emit the signal | ||||
|         if (!this._checkBox.actor.visible) { | ||||
|         if (!this._checkBox.visible) { | ||||
|             callback(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Trigger the offline update as requested | ||||
|         if (this._checkBox.actor.checked) { | ||||
|         if (this._checkBox.checked) { | ||||
|             switch (signal) { | ||||
|             case "ConfirmedReboot": | ||||
|                 this._triggerOfflineUpdateReboot(callback); | ||||
| @@ -567,7 +566,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|  | ||||
|         this._timerId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => { | ||||
|             let currentTime = GLib.get_monotonic_time(); | ||||
|             let secondsElapsed = ((currentTime - startTime) / 1000000); | ||||
|             let secondsElapsed = (currentTime - startTime) / 1000000; | ||||
|  | ||||
|             this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed; | ||||
|             if (this._secondsLeft > 0) { | ||||
| @@ -656,7 +655,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|  | ||||
|         let actor = new St.BoxLayout({ style_class: 'end-session-dialog-session-list-item', | ||||
|                                        can_focus: true }); | ||||
|         actor.add(avatar.actor); | ||||
|         actor.add(avatar); | ||||
|  | ||||
|         let nameLabel = new St.Label({ text: userLabelText, | ||||
|                                        style_class: 'end-session-dialog-session-list-item-name', | ||||
| @@ -682,11 +681,12 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|                     continue; | ||||
|  | ||||
|                 let sessionId = GLib.getenv('XDG_SESSION_ID'); | ||||
|                 if (!sessionId) | ||||
|                 if (!sessionId) { | ||||
|                     this._loginManager.getCurrentSessionProxy(currentSessionProxy => { | ||||
|                         sessionId = currentSessionProxy.Id; | ||||
|                         log(`endSessionDialog: No XDG_SESSION_ID, fetched from logind: ${sessionId}`); | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
|                 if (proxy.Id == sessionId) | ||||
|                     continue; | ||||
| @@ -754,14 +754,14 @@ class EndSessionDialog extends ModalDialog.ModalDialog { | ||||
|         let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed; | ||||
|  | ||||
|         _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || ''); | ||||
|         this._checkBox.actor.visible = (dialogContent.checkBoxText && updatePrepared && updatesAllowed); | ||||
|         this._checkBox.actor.checked = (updatePrepared && updateTriggered); | ||||
|         this._checkBox.visible = dialogContent.checkBoxText && updatePrepared && updatesAllowed; | ||||
|         this._checkBox.checked = updatePrepared && updateTriggered; | ||||
|  | ||||
|         // We show the warning either together with the checkbox, or when | ||||
|         // updates have already been triggered, but the user doesn't have | ||||
|         // enough permissions to cancel them. | ||||
|         this._batteryWarning.visible = (dialogContent.showBatteryWarning && | ||||
|                                         (this._checkBox.actor.visible || updatePrepared && updateTriggered && !updatesAllowed)); | ||||
|         this._batteryWarning.visible = dialogContent.showBatteryWarning && | ||||
|                                         (this._checkBox.visible || updatePrepared && updateTriggered && !updatesAllowed); | ||||
|  | ||||
|         this._updateButtons(); | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ imports.gi.versions.Gtk = '3.0'; | ||||
| imports.gi.versions.TelepathyGLib = '0.12'; | ||||
| imports.gi.versions.TelepathyLogger = '0.2'; | ||||
|  | ||||
| const { Clutter, GLib, Meta, Shell, St } = imports.gi; | ||||
| const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi; | ||||
| const Gettext = imports.gettext; | ||||
|  | ||||
| // We can't import shell JS modules yet, because they may have | ||||
| @@ -23,7 +23,7 @@ const Gettext = imports.gettext; | ||||
| // of interfaces in Javascript | ||||
| function _patchContainerClass(containerClass) { | ||||
|     // This one is a straightforward mapping of the C method | ||||
|     containerClass.prototype.child_set = function(actor, props) { | ||||
|     containerClass.prototype.child_set = function (actor, props) { | ||||
|         let meta = this.get_child_meta(actor); | ||||
|         for (let prop in props) | ||||
|             meta[prop] = props[prop]; | ||||
| @@ -32,7 +32,7 @@ function _patchContainerClass(containerClass) { | ||||
|     // clutter_container_add() actually is a an add-many-actors | ||||
|     // method. We conveniently, but somewhat dubiously, take the | ||||
|     // this opportunity to make it do something more useful. | ||||
|     containerClass.prototype.add = function(actor, props) { | ||||
|     containerClass.prototype.add = function (actor, props) { | ||||
|         this.add_actor(actor); | ||||
|         if (props) | ||||
|             this.child_set(actor, props); | ||||
| @@ -40,8 +40,8 @@ function _patchContainerClass(containerClass) { | ||||
| } | ||||
|  | ||||
| function _patchLayoutClass(layoutClass, styleProps) { | ||||
|     if (styleProps) | ||||
|         layoutClass.prototype.hookup_style = function(container) { | ||||
|     if (styleProps) { | ||||
|         layoutClass.prototype.hookup_style = function (container) { | ||||
|             container.connect('style-changed', () => { | ||||
|                 let node = container.get_theme_node(); | ||||
|                 for (let prop in styleProps) { | ||||
| @@ -51,11 +51,7 @@ function _patchLayoutClass(layoutClass, styleProps) { | ||||
|                 } | ||||
|             }); | ||||
|         }; | ||||
|     layoutClass.prototype.child_set = function(actor, props) { | ||||
|         let meta = this.get_child_meta(actor.get_parent(), actor); | ||||
|         for (let prop in props) | ||||
|             meta[prop] = props[prop]; | ||||
|     }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function _makeEaseCallback(params, cleanup) { | ||||
| @@ -105,12 +101,20 @@ function _easeActor(actor, params) { | ||||
|         actor.set_easing_delay(params.delay); | ||||
|     delete params.delay; | ||||
|  | ||||
|     let repeatCount = 0; | ||||
|     if (params.repeatCount != undefined) | ||||
|         repeatCount = params.repeatCount; | ||||
|     delete params.repeatCount; | ||||
|  | ||||
|     let autoReverse = false; | ||||
|     if (params.autoReverse != undefined) | ||||
|         autoReverse = params.autoReverse; | ||||
|     delete params.autoReverse; | ||||
|  | ||||
|     if (params.mode != undefined) | ||||
|         actor.set_easing_mode(params.mode); | ||||
|     delete params.mode; | ||||
|  | ||||
|     Meta.disable_unredirect_for_display(global.display); | ||||
|  | ||||
|     let cleanup = () => Meta.enable_unredirect_for_display(global.display); | ||||
|     let callback = _makeEaseCallback(params, cleanup); | ||||
|  | ||||
| @@ -124,10 +128,17 @@ function _easeActor(actor, params) { | ||||
|     let transition = animatedProps.map(p => actor.get_transition(p)) | ||||
|         .find(t => t !== null); | ||||
|  | ||||
|     if (transition) | ||||
|         transition.connect('stopped', (t, finished) => callback(finished)); | ||||
|     if (transition && transition.delay) | ||||
|         transition.connect('started', () => Meta.disable_unredirect_for_display(global.display)); | ||||
|     else | ||||
|         Meta.disable_unredirect_for_display(global.display); | ||||
|  | ||||
|     if (transition) { | ||||
|         transition.set({ repeatCount, autoReverse }); | ||||
|         transition.connect('stopped', (t, finished) => callback(finished)); | ||||
|     } else { | ||||
|         callback(true); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function _easeActorProperty(actor, propName, target, params) { | ||||
| @@ -140,13 +151,21 @@ function _easeActorProperty(actor, propName, target, params) { | ||||
|         params.duration = adjustAnimationTime(params.duration); | ||||
|     let duration = Math.floor(params.duration || 0); | ||||
|  | ||||
|     let repeatCount = 0; | ||||
|     if (params.repeatCount != undefined) | ||||
|         repeatCount = params.repeatCount; | ||||
|     delete params.repeatCount; | ||||
|  | ||||
|     let autoReverse = false; | ||||
|     if (params.autoReverse != undefined) | ||||
|         autoReverse = params.autoReverse; | ||||
|     delete params.autoReverse; | ||||
|  | ||||
|     // Copy Clutter's behavior for implicit animations, see | ||||
|     // should_skip_implicit_transition() | ||||
|     if (actor instanceof Clutter.Actor && !actor.mapped) | ||||
|         duration = 0; | ||||
|  | ||||
|     Meta.disable_unredirect_for_display(global.display); | ||||
|  | ||||
|     let cleanup = () => Meta.enable_unredirect_for_display(global.display); | ||||
|     let callback = _makeEaseCallback(params, cleanup); | ||||
|  | ||||
| @@ -157,6 +176,7 @@ function _easeActorProperty(actor, propName, target, params) { | ||||
|         let [obj, prop] = _getPropertyTarget(actor, propName); | ||||
|         obj[prop] = target; | ||||
|  | ||||
|         Meta.disable_unredirect_for_display(global.display); | ||||
|         callback(true); | ||||
|  | ||||
|         return; | ||||
| @@ -166,12 +186,19 @@ function _easeActorProperty(actor, propName, target, params) { | ||||
|     let transition = new Clutter.PropertyTransition(Object.assign({ | ||||
|         property_name: propName, | ||||
|         interval: new Clutter.Interval({ value_type: pspec.value_type }), | ||||
|         remove_on_complete: true | ||||
|         remove_on_complete: true, | ||||
|         repeat_count: repeatCount, | ||||
|         auto_reverse: autoReverse, | ||||
|     }, params)); | ||||
|     actor.add_transition(propName, transition); | ||||
|  | ||||
|     transition.set_to(target); | ||||
|  | ||||
|     if (transition.delay) | ||||
|         transition.connect('started', () => Meta.disable_unredirect_for_display(global.display)); | ||||
|     else | ||||
|         Meta.disable_unredirect_for_display(global.display); | ||||
|  | ||||
|     transition.connect('stopped', (t, finished) => callback(finished)); | ||||
| } | ||||
|  | ||||
| @@ -202,37 +229,37 @@ function init() { | ||||
|     window.ngettext = Gettext.ngettext; | ||||
|     window.N_ = s => s; | ||||
|  | ||||
|     GObject.gtypeNameBasedOnJSPath = true; | ||||
|  | ||||
|     // Miscellaneous monkeypatching | ||||
|     _patchContainerClass(St.BoxLayout); | ||||
|  | ||||
|     _patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows', | ||||
|                                              column_spacing: 'spacing-columns' }); | ||||
|     _patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows', | ||||
|                                             column_spacing: 'spacing-columns' }); | ||||
|     _patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' }); | ||||
|  | ||||
|     let origSetEasingDuration = Clutter.Actor.prototype.set_easing_duration; | ||||
|     Clutter.Actor.prototype.set_easing_duration = function(msecs) { | ||||
|     Clutter.Actor.prototype.set_easing_duration = function (msecs) { | ||||
|         origSetEasingDuration.call(this, adjustAnimationTime(msecs)); | ||||
|     }; | ||||
|     let origSetEasingDelay = Clutter.Actor.prototype.set_easing_delay; | ||||
|     Clutter.Actor.prototype.set_easing_delay = function(msecs) { | ||||
|     Clutter.Actor.prototype.set_easing_delay = function (msecs) { | ||||
|         origSetEasingDelay.call(this, adjustAnimationTime(msecs)); | ||||
|     }; | ||||
|  | ||||
|     Clutter.Actor.prototype.ease = function(props, easingParams) { | ||||
|     Clutter.Actor.prototype.ease = function (props, easingParams) { | ||||
|         _easeActor(this, props, easingParams); | ||||
|     }; | ||||
|     Clutter.Actor.prototype.ease_property = function(propName, target, params) { | ||||
|     Clutter.Actor.prototype.ease_property = function (propName, target, params) { | ||||
|         _easeActorProperty(this, propName, target, params); | ||||
|     }; | ||||
|     St.Adjustment.prototype.ease = function(target, params) { | ||||
|     St.Adjustment.prototype.ease = function (target, params) { | ||||
|         // we're not an actor of course, but we implement the same | ||||
|         // transition API as Clutter.Actor, so this works anyway | ||||
|         _easeActorProperty(this, 'value', target, params); | ||||
|     }; | ||||
|  | ||||
|     Clutter.Actor.prototype.toString = function() { | ||||
|     Clutter.Actor.prototype.toString = function () { | ||||
|         return St.describe_actor(this); | ||||
|     }; | ||||
|     // Deprecation warning for former JS classes turned into an actor subclass | ||||
| @@ -242,17 +269,17 @@ function init() { | ||||
|             let { stack } = new Error(); | ||||
|             log(`Usage of object.actor is deprecated for ${klass}\n${stack}`); | ||||
|             return this; | ||||
|         } | ||||
|         }, | ||||
|     }); | ||||
|  | ||||
|     St.set_slow_down_factor = function(factor) { | ||||
|     St.set_slow_down_factor = function (factor) { | ||||
|         let { stack } = new Error(); | ||||
|         log(`St.set_slow_down_factor() is deprecated, use St.Settings.slow_down_factor\n${stack}`); | ||||
|         St.Settings.get().slow_down_factor = factor; | ||||
|     }; | ||||
|  | ||||
|     let origToString = Object.prototype.toString; | ||||
|     Object.prototype.toString = function() { | ||||
|     Object.prototype.toString = function () { | ||||
|         let base = origToString.call(this); | ||||
|         try { | ||||
|             if ('actor' in this && this.actor instanceof Clutter.Actor) | ||||
| @@ -265,8 +292,9 @@ function init() { | ||||
|     }; | ||||
|  | ||||
|     // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783 | ||||
|     Date.prototype.toLocaleFormat = function(format) { | ||||
|         return Shell.util_format_date(format, this.getTime()); | ||||
|     Date.prototype.toLocaleFormat = function (format) { | ||||
|         let dt = GLib.DateTime.new_from_unix_local(this.getTime() / 1000); | ||||
|         return dt ? dt.format(format) : ''; | ||||
|     }; | ||||
|  | ||||
|     let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR'); | ||||
|   | ||||
| @@ -19,12 +19,12 @@ var REPOSITORY_URL_UPDATE   = `${REPOSITORY_URL_BASE}/update-info/`; | ||||
| let _httpSession; | ||||
|  | ||||
| function installExtension(uuid, invocation) { | ||||
|     let params = { uuid: uuid, | ||||
|     let params = { uuid, | ||||
|                    shell_version: Config.PACKAGE_VERSION }; | ||||
|  | ||||
|     let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params); | ||||
|  | ||||
|     _httpSession.queue_message(message, (session, message) => { | ||||
|     _httpSession.queue_message(message, () => { | ||||
|         if (message.status_code != Soup.KnownStatusCode.OK) { | ||||
|             Main.extensionManager.logExtensionError(uuid, `downloading info: ${message.status_code}`); | ||||
|             invocation.return_dbus_error('org.gnome.Shell.DownloadInfoError', message.status_code.toString()); | ||||
| @@ -90,7 +90,7 @@ function gotExtensionZipFile(session, message, uuid, dir, callback, errback) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, (pid, status) => { | ||||
|     GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, (o, status) => { | ||||
|         GLib.spawn_close_pid(pid); | ||||
|  | ||||
|         if (status != 0) | ||||
| @@ -113,7 +113,7 @@ function updateExtension(uuid) { | ||||
|     let url = REPOSITORY_URL_DOWNLOAD.format(uuid); | ||||
|     let message = Soup.form_request_new_from_hash('GET', url, params); | ||||
|  | ||||
|     _httpSession.queue_message(message, (session, message) => { | ||||
|     _httpSession.queue_message(message, session => { | ||||
|         gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, () => { | ||||
|             let oldExtension = Main.extensionManager.lookup(uuid); | ||||
|             let extensionDir = oldExtension.dir; | ||||
| @@ -145,8 +145,8 @@ function updateExtension(uuid) { | ||||
|             } | ||||
|  | ||||
|             FileUtils.recursivelyDeleteDir(oldExtensionTmpDir, true); | ||||
|         }, (code, message) => { | ||||
|             log('Error while updating extension %s: %s (%s)'.format(uuid, code, message ? message : '')); | ||||
|         }, (code, msg) => { | ||||
|             log(`Error while updating extension ${uuid}: ${code} (${msg})`); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
| @@ -162,7 +162,7 @@ function checkForUpdates() { | ||||
|  | ||||
|     let url = REPOSITORY_URL_UPDATE; | ||||
|     let message = Soup.form_request_new_from_hash('GET', url, params); | ||||
|     _httpSession.queue_message(message, (session, message) => { | ||||
|     _httpSession.queue_message(message, () => { | ||||
|         if (message.status_code != Soup.KnownStatusCode.OK) | ||||
|             return; | ||||
|  | ||||
| @@ -189,7 +189,7 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog { | ||||
|         this.setButtons([{ | ||||
|             label: _("Cancel"), | ||||
|             action: this._onCancelButtonPressed.bind(this), | ||||
|             key: Clutter.Escape, | ||||
|             key: Clutter.KEY_Escape, | ||||
|         }, { | ||||
|             label: _("Install"), | ||||
|             action: this._onInstallButtonPressed.bind(this), | ||||
| @@ -199,8 +199,8 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog { | ||||
|         let content = new Dialog.MessageDialogContent({ | ||||
|             title: _("Download and install “%s” from extensions.gnome.org?").format(info.name), | ||||
|             icon: new Gio.FileIcon({ | ||||
|                 file: Gio.File.new_for_uri(`${REPOSITORY_URL_BASE}${info.icon}`) | ||||
|             }) | ||||
|                 file: Gio.File.new_for_uri(`${REPOSITORY_URL_BASE}${info.icon}`), | ||||
|             }), | ||||
|         }); | ||||
|  | ||||
|         this.contentLayout.add(content); | ||||
| @@ -220,10 +220,9 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog { | ||||
|         let uuid = this._uuid; | ||||
|         let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid])); | ||||
|         let invocation = this._invocation; | ||||
|         function errback(code, message) { | ||||
|             let msg = message ? message.toString() : ''; | ||||
|             log('Error while installing %s: %s (%s)'.format(uuid, code, msg)); | ||||
|             invocation.return_dbus_error(`org.gnome.Shell.${code}`, msg); | ||||
|         function errback(code, msg) { | ||||
|             log(`Error while installing ${uuid}: ${code} (${msg})`); | ||||
|             invocation.return_dbus_error(`org.gnome.Shell.${code}`, msg || ''); | ||||
|         } | ||||
|  | ||||
|         function callback() { | ||||
| @@ -241,7 +240,7 @@ class InstallExtensionDialog extends ModalDialog.ModalDialog { | ||||
|             invocation.return_value(GLib.Variant.new('(s)', ['successful'])); | ||||
|         } | ||||
|  | ||||
|         _httpSession.queue_message(message, (session, message) => { | ||||
|         _httpSession.queue_message(message, session => { | ||||
|             gotExtensionZipFile(session, message, uuid, dir, callback, errback); | ||||
|         }); | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported init connect disconnect */ | ||||
|  | ||||
| const { Gio, St } = imports.gi; | ||||
| const { GLib, Gio, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
| @@ -28,6 +28,23 @@ var ExtensionManager = class { | ||||
|     } | ||||
|  | ||||
|     init() { | ||||
|         // The following file should exist for a period of time when extensions | ||||
|         // are enabled after start. If it exists, then the systemd unit will | ||||
|         // disable extensions should gnome-shell crash. | ||||
|         // Should the file already exist from a previous login, then this is OK. | ||||
|         let disableFilename = GLib.build_filenamev([GLib.get_user_runtime_dir(), 'gnome-shell-disable-extensions']); | ||||
|         let disableFile = Gio.File.new_for_path(disableFilename); | ||||
|         try { | ||||
|             disableFile.create(Gio.FileCreateFlags.REPLACE_DESTINATION, null); | ||||
|         } catch (e) { | ||||
|             log(`Failed to create file ${disableFilename}: ${e.message}`); | ||||
|         } | ||||
|  | ||||
|         GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 60, () => { | ||||
|             disableFile.delete(null); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|  | ||||
|         this._sessionUpdated(); | ||||
|     } | ||||
|  | ||||
| @@ -60,11 +77,11 @@ var ExtensionManager = class { | ||||
|         let orderReversed = order.slice().reverse(); | ||||
|  | ||||
|         for (let i = 0; i < orderReversed.length; i++) { | ||||
|             let uuid = orderReversed[i]; | ||||
|             let otherUuid = orderReversed[i]; | ||||
|             try { | ||||
|                 this.lookup(uuid).stateObj.disable(); | ||||
|                 this.lookup(otherUuid).stateObj.disable(); | ||||
|             } catch (e) { | ||||
|                 this.logExtensionError(uuid, e); | ||||
|                 this.logExtensionError(otherUuid, e); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -81,11 +98,11 @@ var ExtensionManager = class { | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < order.length; i++) { | ||||
|             let uuid = order[i]; | ||||
|             let otherUuid = order[i]; | ||||
|             try { | ||||
|                 this.lookup(uuid).stateObj.enable(); | ||||
|                 this.lookup(otherUuid).stateObj.enable(); | ||||
|             } catch (e) { | ||||
|                 this.logExtensionError(uuid, e); | ||||
|                 this.logExtensionError(otherUuid, e); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -200,9 +217,8 @@ var ExtensionManager = class { | ||||
|  | ||||
|     createExtensionObject(uuid, dir, type) { | ||||
|         let metadataFile = dir.get_child('metadata.json'); | ||||
|         if (!metadataFile.query_exists(null)) { | ||||
|         if (!metadataFile.query_exists(null)) | ||||
|             throw new Error('Missing metadata.json'); | ||||
|         } | ||||
|  | ||||
|         let metadataContents, success_; | ||||
|         try { | ||||
| @@ -222,14 +238,12 @@ var ExtensionManager = class { | ||||
|         let requiredProperties = ['uuid', 'name', 'description', 'shell-version']; | ||||
|         for (let i = 0; i < requiredProperties.length; i++) { | ||||
|             let prop = requiredProperties[i]; | ||||
|             if (!meta[prop]) { | ||||
|             if (!meta[prop]) | ||||
|                 throw new Error(`missing "${prop}" property in metadata.json`); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (uuid != meta.uuid) { | ||||
|         if (uuid != meta.uuid) | ||||
|             throw new Error(`uuid "${meta.uuid}" from metadata.json does not match directory name "${uuid}"`); | ||||
|         } | ||||
|  | ||||
|         let extension = { | ||||
|             metadata: meta, | ||||
| @@ -239,7 +253,7 @@ var ExtensionManager = class { | ||||
|             path: dir.get_path(), | ||||
|             error: '', | ||||
|             hasPrefs: dir.get_child('prefs.js').query_exists(null), | ||||
|             canChange: false | ||||
|             canChange: false, | ||||
|         }; | ||||
|         this._extensions.set(uuid, extension); | ||||
|  | ||||
|   | ||||
| @@ -195,7 +195,7 @@ var GrabHelper = class GrabHelper { | ||||
|     } | ||||
|  | ||||
|     _takeModalGrab() { | ||||
|         let firstGrab = (this._modalCount == 0); | ||||
|         let firstGrab = this._modalCount == 0; | ||||
|         if (firstGrab) { | ||||
|             if (!Main.pushModal(this._owner, this._modalParams)) | ||||
|                 return false; | ||||
| @@ -292,7 +292,7 @@ var GrabHelper = class GrabHelper { | ||||
|         let touchEnd = type == Clutter.EventType.TOUCH_END; | ||||
|         let touch = touchUpdate || touchBegin || touchEnd; | ||||
|  | ||||
|         if (touch && !global.display.is_pointer_emulating_sequence (event.get_event_sequence())) | ||||
|         if (touch && !global.display.is_pointer_emulating_sequence(event.get_event_sequence())) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (this._ignoreUntilRelease && (motion || release || touch)) { | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported CandidatePopup */ | ||||
|  | ||||
| const { Clutter, IBus, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
| const { Clutter, GObject, IBus, St } = imports.gi; | ||||
|  | ||||
| const BoxPointer = imports.ui.boxpointer; | ||||
| const Main = imports.ui.main; | ||||
| @@ -12,11 +11,23 @@ var MAX_CANDIDATES_PER_PAGE = 16; | ||||
| var DEFAULT_INDEX_LABELS = ['1', '2', '3', '4', '5', '6', '7', '8', | ||||
|                             '9', '0', 'a', 'b', 'c', 'd', 'e', 'f']; | ||||
|  | ||||
| var CandidateArea = class CandidateArea { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ vertical: true, | ||||
|                                         reactive: true, | ||||
|                                         visible: false }); | ||||
| var CandidateArea = GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'candidate-clicked': { param_types: [GObject.TYPE_UINT, | ||||
|                                              GObject.TYPE_UINT, | ||||
|                                              Clutter.ModifierType.$gtype] }, | ||||
|         'cursor-down': {}, | ||||
|         'cursor-up': {}, | ||||
|         'next-page': {}, | ||||
|         'previous-page': {}, | ||||
|     }, | ||||
| }, class CandidateArea extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             vertical: true, | ||||
|             reactive: true, | ||||
|             visible: false, | ||||
|         }); | ||||
|         this._candidateBoxes = []; | ||||
|         for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { | ||||
|             let box = new St.BoxLayout({ style_class: 'candidate-box', | ||||
| @@ -24,10 +35,10 @@ var CandidateArea = class CandidateArea { | ||||
|                                          track_hover: true }); | ||||
|             box._indexLabel = new St.Label({ style_class: 'candidate-index' }); | ||||
|             box._candidateLabel = new St.Label({ style_class: 'candidate-label' }); | ||||
|             box.add(box._indexLabel, { y_fill: false }); | ||||
|             box.add(box._candidateLabel, { y_fill: false }); | ||||
|             box.add_child(box._indexLabel); | ||||
|             box.add_child(box._candidateLabel); | ||||
|             this._candidateBoxes.push(box); | ||||
|             this.actor.add(box); | ||||
|             this.add(box); | ||||
|  | ||||
|             let j = i; | ||||
|             box.connect('button-release-event', (actor, event) => { | ||||
| @@ -36,30 +47,23 @@ var CandidateArea = class CandidateArea { | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         this.actor.connect('scroll-event', (actor, event) => { | ||||
|             let direction = event.get_scroll_direction(); | ||||
|             switch (direction) { | ||||
|             case Clutter.ScrollDirection.UP: | ||||
|                 this.emit('cursor-up'); | ||||
|                 break; | ||||
|             case Clutter.ScrollDirection.DOWN: | ||||
|                 this.emit('cursor-down'); | ||||
|                 break; | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         }); | ||||
|  | ||||
|         this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' }); | ||||
|  | ||||
|         this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous button' }); | ||||
|         this._previousButton = new St.Button({ | ||||
|             style_class: 'candidate-page-button candidate-page-button-previous button', | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' }); | ||||
|         this._buttonBox.add(this._previousButton, { expand: true }); | ||||
|         this._buttonBox.add_child(this._previousButton); | ||||
|  | ||||
|         this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next button' }); | ||||
|         this._nextButton = new St.Button({ | ||||
|             style_class: 'candidate-page-button candidate-page-button-next button', | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' }); | ||||
|         this._buttonBox.add(this._nextButton, { expand: true }); | ||||
|         this._buttonBox.add_child(this._nextButton); | ||||
|  | ||||
|         this.actor.add(this._buttonBox); | ||||
|         this.add(this._buttonBox); | ||||
|  | ||||
|         this._previousButton.connect('clicked', () => { | ||||
|             this.emit('previous-page'); | ||||
| @@ -72,6 +76,18 @@ var CandidateArea = class CandidateArea { | ||||
|         this._cursorPosition = 0; | ||||
|     } | ||||
|  | ||||
|     vfunc_scroll_event(scrollEvent) { | ||||
|         switch (scrollEvent.direction) { | ||||
|         case Clutter.ScrollDirection.UP: | ||||
|             this.emit('cursor-up'); | ||||
|             break; | ||||
|         case Clutter.ScrollDirection.DOWN: | ||||
|             this.emit('cursor-down'); | ||||
|             break; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     setOrientation(orientation) { | ||||
|         if (this._orientation == orientation) | ||||
|             return; | ||||
| @@ -79,15 +95,15 @@ var CandidateArea = class CandidateArea { | ||||
|         this._orientation = orientation; | ||||
|  | ||||
|         if (this._orientation == IBus.Orientation.HORIZONTAL) { | ||||
|             this.actor.vertical = false; | ||||
|             this.actor.remove_style_class_name('vertical'); | ||||
|             this.actor.add_style_class_name('horizontal'); | ||||
|             this.vertical = false; | ||||
|             this.remove_style_class_name('vertical'); | ||||
|             this.add_style_class_name('horizontal'); | ||||
|             this._previousButton.child.icon_name = 'go-previous-symbolic'; | ||||
|             this._nextButton.child.icon_name = 'go-next-symbolic'; | ||||
|         } else {                // VERTICAL || SYSTEM | ||||
|             this.actor.vertical = true; | ||||
|             this.actor.add_style_class_name('vertical'); | ||||
|             this.actor.remove_style_class_name('horizontal'); | ||||
|             this.vertical = true; | ||||
|             this.add_style_class_name('vertical'); | ||||
|             this.remove_style_class_name('horizontal'); | ||||
|             this._previousButton.child.icon_name = 'go-up-symbolic'; | ||||
|             this._nextButton.child.icon_name = 'go-down-symbolic'; | ||||
|         } | ||||
| @@ -102,7 +118,7 @@ var CandidateArea = class CandidateArea { | ||||
|             if (!visible) | ||||
|                 continue; | ||||
|  | ||||
|             box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : DEFAULT_INDEX_LABELS[i]); | ||||
|             box._indexLabel.text = indexes && indexes[i] ? indexes[i] : DEFAULT_INDEX_LABELS[i]; | ||||
|             box._candidateLabel.text = candidates[i]; | ||||
|         } | ||||
|  | ||||
| @@ -121,22 +137,23 @@ var CandidateArea = class CandidateArea { | ||||
|         this._previousButton.reactive = wrapsAround || page > 0; | ||||
|         this._nextButton.reactive = wrapsAround || page < nPages - 1; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(CandidateArea.prototype); | ||||
| }); | ||||
|  | ||||
| var CandidatePopup = GObject.registerClass( | ||||
| class IbusCandidatePopup extends BoxPointer.BoxPointer { | ||||
|     _init() { | ||||
|         super._init(St.Side.TOP); | ||||
|         this.visible = false; | ||||
|         this.style_class = 'candidate-popup-boxpointer'; | ||||
|  | ||||
| var CandidatePopup = class CandidatePopup { | ||||
|     constructor() { | ||||
|         this._dummyCursor = new St.Widget({ opacity: 0 }); | ||||
|         Main.layoutManager.uiGroup.add_actor(this._dummyCursor); | ||||
|  | ||||
|         this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP); | ||||
|         this._boxPointer.visible = false; | ||||
|         this._boxPointer.style_class = 'candidate-popup-boxpointer'; | ||||
|         Main.layoutManager.addChrome(this._boxPointer); | ||||
|         Main.layoutManager.addChrome(this); | ||||
|  | ||||
|         let box = new St.BoxLayout({ style_class: 'candidate-popup-content', | ||||
|                                      vertical: true }); | ||||
|         this._boxPointer.bin.set_child(box); | ||||
|         this.bin.set_child(box); | ||||
|  | ||||
|         this._preeditText = new St.Label({ style_class: 'candidate-popup-text', | ||||
|                                            visible: false }); | ||||
| @@ -147,7 +164,7 @@ var CandidatePopup = class CandidatePopup { | ||||
|         box.add(this._auxText); | ||||
|  | ||||
|         this._candidateArea = new CandidateArea(); | ||||
|         box.add(this._candidateArea.actor); | ||||
|         box.add(this._candidateArea); | ||||
|  | ||||
|         this._candidateArea.connect('previous-page', () => { | ||||
|             this._panelService.page_up(); | ||||
| @@ -198,9 +215,10 @@ var CandidatePopup = class CandidatePopup { | ||||
|             this._preeditText.text = text.get_text(); | ||||
|  | ||||
|             let attrs = text.get_attributes(); | ||||
|             if (attrs) | ||||
|             if (attrs) { | ||||
|                 this._setTextAttributes(this._preeditText.clutter_text, | ||||
|                                         attrs); | ||||
|             } | ||||
|         }); | ||||
|         panelService.connect('show-preedit-text', () => { | ||||
|             this._preeditText.show(); | ||||
| @@ -225,14 +243,14 @@ var CandidatePopup = class CandidatePopup { | ||||
|             this._updateVisibility(); | ||||
|         }); | ||||
|         panelService.connect('update-lookup-table', (_ps, lookupTable, visible) => { | ||||
|             this._candidateArea.actor.visible = visible; | ||||
|             this._candidateArea.visible = visible; | ||||
|             this._updateVisibility(); | ||||
|  | ||||
|             let nCandidates = lookupTable.get_number_of_candidates(); | ||||
|             let cursorPos = lookupTable.get_cursor_pos(); | ||||
|             let pageSize = lookupTable.get_page_size(); | ||||
|             let nPages = Math.ceil(nCandidates / pageSize); | ||||
|             let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize)); | ||||
|             let page = cursorPos == 0 ? 0 : Math.floor(cursorPos / pageSize); | ||||
|             let startIndex = page * pageSize; | ||||
|             let endIndex = Math.min((page + 1) * pageSize, nCandidates); | ||||
|  | ||||
| @@ -261,15 +279,15 @@ var CandidatePopup = class CandidatePopup { | ||||
|             this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages); | ||||
|         }); | ||||
|         panelService.connect('show-lookup-table', () => { | ||||
|             this._candidateArea.actor.show(); | ||||
|             this._candidateArea.show(); | ||||
|             this._updateVisibility(); | ||||
|         }); | ||||
|         panelService.connect('hide-lookup-table', () => { | ||||
|             this._candidateArea.actor.hide(); | ||||
|             this._candidateArea.hide(); | ||||
|             this._updateVisibility(); | ||||
|         }); | ||||
|         panelService.connect('focus-out', () => { | ||||
|             this._boxPointer.close(BoxPointer.PopupAnimation.NONE); | ||||
|             this.close(BoxPointer.PopupAnimation.NONE); | ||||
|             Main.keyboard.resetSuggestions(); | ||||
|         }); | ||||
|     } | ||||
| @@ -278,29 +296,30 @@ var CandidatePopup = class CandidatePopup { | ||||
|         this._dummyCursor.set_position(Math.round(x), Math.round(y)); | ||||
|         this._dummyCursor.set_size(Math.round(w), Math.round(h)); | ||||
|  | ||||
|         if (this._boxPointer.visible) | ||||
|             this._boxPointer.setPosition(this._dummyCursor, 0); | ||||
|         if (this.visible) | ||||
|             this.setPosition(this._dummyCursor, 0); | ||||
|     } | ||||
|  | ||||
|     _updateVisibility() { | ||||
|         let isVisible = (!Main.keyboard.visible && | ||||
|         let isVisible = !Main.keyboard.visible && | ||||
|                          (this._preeditText.visible || | ||||
|                           this._auxText.visible || | ||||
|                           this._candidateArea.actor.visible)); | ||||
|                           this._candidateArea.visible); | ||||
|  | ||||
|         if (isVisible) { | ||||
|             this._boxPointer.setPosition(this._dummyCursor, 0); | ||||
|             this._boxPointer.open(BoxPointer.PopupAnimation.NONE); | ||||
|             this._boxPointer.raise_top(); | ||||
|             this.setPosition(this._dummyCursor, 0); | ||||
|             this.open(BoxPointer.PopupAnimation.NONE); | ||||
|             this.get_parent().set_child_above_sibling(this, null); | ||||
|         } else { | ||||
|             this._boxPointer.close(BoxPointer.PopupAnimation.NONE); | ||||
|             this.close(BoxPointer.PopupAnimation.NONE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _setTextAttributes(clutterText, ibusAttrList) { | ||||
|         let attr; | ||||
|         for (let i = 0; (attr = ibusAttrList.get(i)); ++i) | ||||
|         for (let i = 0; (attr = ibusAttrList.get(i)); ++i) { | ||||
|             if (attr.get_attr_type() == IBus.AttrType.BACKGROUND) | ||||
|                 clutterText.set_selection(attr.get_start_index(), attr.get_end_index()); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported BaseIcon, IconGrid, PaginatedIconGrid */ | ||||
|  | ||||
| const { Clutter, GLib, GObject, Meta, St } = imports.gi; | ||||
| const { Clutter, GLib, GObject, Graphene, Meta, St } = imports.gi; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
| const Main = imports.ui.main; | ||||
| @@ -22,7 +22,7 @@ var ANIMATION_BOUNCE_ICON_SCALE = 1.1; | ||||
|  | ||||
| var AnimationDirection = { | ||||
|     IN: 0, | ||||
|     OUT: 1 | ||||
|     OUT: 1, | ||||
| }; | ||||
|  | ||||
| var APPICON_ANIMATION_OUT_SCALE = 3; | ||||
| @@ -39,18 +39,19 @@ class BaseIcon extends St.Bin { | ||||
|         if (params.showLabel) | ||||
|             styleClass += ' overview-icon-with-label'; | ||||
|  | ||||
|         super._init({ style_class: styleClass, | ||||
|                       x_fill: true, | ||||
|                       y_fill: true }); | ||||
|         super._init({ style_class: styleClass }); | ||||
|  | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|  | ||||
|         this._box = new St.BoxLayout({ vertical: true }); | ||||
|         this._box = new St.BoxLayout({ | ||||
|             vertical: true, | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this.set_child(this._box); | ||||
|  | ||||
|         this.iconSize = ICON_SIZE; | ||||
|         this._iconBin = new St.Bin({ x_align: St.Align.MIDDLE, | ||||
|                                      y_align: St.Align.MIDDLE }); | ||||
|         this._iconBin = new St.Bin(); | ||||
|  | ||||
|         this._box.add_actor(this._iconBin); | ||||
|  | ||||
| @@ -58,7 +59,7 @@ class BaseIcon extends St.Bin { | ||||
|             this.label = new St.Label({ text: label }); | ||||
|             this.label.clutter_text.set({ | ||||
|                 x_align: Clutter.ActorAlign.CENTER, | ||||
|                 y_align: Clutter.ActorAlign.CENTER | ||||
|                 y_align: Clutter.ActorAlign.CENTER, | ||||
|             }); | ||||
|             this._box.add_actor(this.label); | ||||
|         } else { | ||||
| @@ -189,7 +190,7 @@ function zoomOutActorAtPos(actor, x, y) { | ||||
|         opacity: 0, | ||||
|         duration: APPICON_ANIMATION_OUT_TIME, | ||||
|         mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         onComplete: () => actorClone.destroy() | ||||
|         onComplete: () => actorClone.destroy(), | ||||
|     }); | ||||
| } | ||||
|  | ||||
| @@ -231,21 +232,21 @@ var IconGrid = GObject.registerClass({ | ||||
|         this._fixedHItemSize = this._fixedVItemSize = undefined; | ||||
|         this.connect('style-changed', this._onStyleChanged.bind(this)); | ||||
|  | ||||
|         // Cancel animations when hiding the overview, to avoid icons | ||||
|         // swarming into the void ... | ||||
|         this.connect('notify::mapped', () => { | ||||
|             if (!this.mapped) | ||||
|                 this._resetAnimationActors(); | ||||
|         }); | ||||
|  | ||||
|         this.connect('actor-added', this._childAdded.bind(this)); | ||||
|         this.connect('actor-removed', this._childRemoved.bind(this)); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|     vfunc_unmap() { | ||||
|         // Cancel animations when hiding the overview, to avoid icons | ||||
|         // swarming into the void ... | ||||
|         this._resetAnimationActors(); | ||||
|         super.vfunc_unmap(); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
|         if (this._updateIconSizesLaterId) { | ||||
|             Meta.later_remove (this._updateIconSizesLaterId); | ||||
|             Meta.later_remove(this._updateIconSizesLaterId); | ||||
|             this._updateIconSizesLaterId = 0; | ||||
|         } | ||||
|     } | ||||
| @@ -402,7 +403,7 @@ var IconGrid = GObject.registerClass({ | ||||
|         let allocationBox = this.get_allocation_box(); | ||||
|         let paintBox = themeNode.get_paint_box(allocationBox); | ||||
|  | ||||
|         let origin = new Clutter.Vertex(); | ||||
|         let origin = new Graphene.Point3D(); | ||||
|         origin.x = paintBox.x1 - allocationBox.x1; | ||||
|         origin.y = paintBox.y1 - allocationBox.y1; | ||||
|         origin.z = 0.0; | ||||
| @@ -431,7 +432,7 @@ var IconGrid = GObject.registerClass({ | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|     /* | ||||
|      * Intended to be override by subclasses if they need a different | ||||
|      * set of items to be animated. | ||||
|      */ | ||||
| @@ -454,9 +455,10 @@ var IconGrid = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     animatePulse(animationDirection) { | ||||
|         if (animationDirection != AnimationDirection.IN) | ||||
|         if (animationDirection != AnimationDirection.IN) { | ||||
|             throw new GObject.NotImplementedError("Pulse animation only implements " + | ||||
|                                                   "'in' animation direction"); | ||||
|         } | ||||
|  | ||||
|         this._resetAnimationActors(); | ||||
|  | ||||
| @@ -486,7 +488,7 @@ var IconGrid = GObject.registerClass({ | ||||
|                 scale_y: ANIMATION_BOUNCE_ICON_SCALE, | ||||
|                 duration: bounceUpTime, | ||||
|                 mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, | ||||
|                 delay: delay, | ||||
|                 delay, | ||||
|                 onComplete: () => { | ||||
|                     let duration = ANIMATION_TIME_IN - bounceUpTime; | ||||
|                     actor.ease({ | ||||
| @@ -498,9 +500,9 @@ var IconGrid = GObject.registerClass({ | ||||
|                             if (isLastItem) | ||||
|                                 this._animationDone(); | ||||
|                             actor.reactive = true; | ||||
|                         } | ||||
|                         }, | ||||
|                     }); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -567,7 +569,7 @@ var IconGrid = GObject.registerClass({ | ||||
|                     scale_y: 1, | ||||
|                     duration: ANIMATION_TIME_IN, | ||||
|                     mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, | ||||
|                     delay | ||||
|                     delay, | ||||
|                 }; | ||||
|  | ||||
|                 if (isLastItem) | ||||
| @@ -577,7 +579,7 @@ var IconGrid = GObject.registerClass({ | ||||
|                     opacity: 255, | ||||
|                     duration: ANIMATION_FADE_IN_TIME_FOR_ITEM, | ||||
|                     mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, | ||||
|                     delay | ||||
|                     delay, | ||||
|                 }; | ||||
|             } else { | ||||
|                 let isLastItem = actor._distance == maxDist; | ||||
| @@ -593,7 +595,7 @@ var IconGrid = GObject.registerClass({ | ||||
|                     scale_y: scaleY, | ||||
|                     duration: ANIMATION_TIME_OUT, | ||||
|                     mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, | ||||
|                     delay | ||||
|                     delay, | ||||
|                 }; | ||||
|  | ||||
|                 if (isLastItem) | ||||
| @@ -603,7 +605,7 @@ var IconGrid = GObject.registerClass({ | ||||
|                     opacity: 0, | ||||
|                     duration: ANIMATION_FADE_IN_TIME_FOR_ITEM, | ||||
|                     mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, | ||||
|                     delay: ANIMATION_TIME_OUT + delay - ANIMATION_FADE_IN_TIME_FOR_ITEM | ||||
|                     delay: ANIMATION_TIME_OUT + delay - ANIMATION_FADE_IN_TIME_FOR_ITEM, | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
| @@ -676,8 +678,8 @@ var IconGrid = GObject.registerClass({ | ||||
|  | ||||
|     nRows(forWidth) { | ||||
|         let children = this._getVisibleChildren(); | ||||
|         let nColumns = (forWidth < 0) ? children.length : this._computeLayout(forWidth)[0]; | ||||
|         let nRows = (nColumns > 0) ? Math.ceil(children.length / nColumns) : 0; | ||||
|         let nColumns = forWidth < 0 ? children.length : this._computeLayout(forWidth)[0]; | ||||
|         let nRows = nColumns > 0 ? Math.ceil(children.length / nColumns) : 0; | ||||
|         if (this._rowLimit) | ||||
|             nRows = Math.min(nRows, this._rowLimit); | ||||
|         return nRows; | ||||
| @@ -717,13 +719,13 @@ var IconGrid = GObject.registerClass({ | ||||
|  | ||||
|         this._items.push(item); | ||||
|         if (index !== undefined) | ||||
|             this.insert_child_at_index(item.actor, index); | ||||
|             this.insert_child_at_index(item, index); | ||||
|         else | ||||
|             this.add_actor(item.actor); | ||||
|             this.add_actor(item); | ||||
|     } | ||||
|  | ||||
|     removeItem(item) { | ||||
|         this.remove_child(item.actor); | ||||
|         this.remove_child(item); | ||||
|     } | ||||
|  | ||||
|     getItemAtIndex(index) { | ||||
| @@ -783,7 +785,7 @@ var IconGrid = GObject.registerClass({ | ||||
|             this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|     /* | ||||
|      * This function must to be called before iconGrid allocation, | ||||
|      * to know how much spacing can the grid has | ||||
|      */ | ||||
| @@ -796,7 +798,7 @@ var IconGrid = GObject.registerClass({ | ||||
|             let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth; | ||||
|             let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight; | ||||
|  | ||||
|             let neededSpacePerItem = (neededWidth > neededHeight) | ||||
|             let neededSpacePerItem = neededWidth > neededHeight | ||||
|                 ? Math.ceil(neededWidth / this._minColumns) | ||||
|                 : Math.ceil(neededHeight / this._minRows); | ||||
|             this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE); | ||||
| @@ -804,9 +806,10 @@ var IconGrid = GObject.registerClass({ | ||||
|  | ||||
|             this._updateSpacingForSize(availWidth, availHeight); | ||||
|         } | ||||
|         if (!this._updateIconSizesLaterId) | ||||
|         if (!this._updateIconSizesLaterId) { | ||||
|             this._updateIconSizesLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, | ||||
|                                                           this._updateIconSizes.bind(this)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up | ||||
| @@ -814,9 +817,9 @@ var IconGrid = GObject.registerClass({ | ||||
|         this._updateIconSizesLaterId = 0; | ||||
|         let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize); | ||||
|         let newIconSize = Math.floor(ICON_SIZE * scale); | ||||
|         for (let i in this._items) { | ||||
|         for (let i in this._items) | ||||
|             this._items[i].icon.setIconSize(newIconSize); | ||||
|         } | ||||
|  | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     } | ||||
| }); | ||||
| @@ -878,9 +881,9 @@ var PaginatedIconGrid = GObject.registerClass({ | ||||
|             children[i].show(); | ||||
|  | ||||
|             columnIndex++; | ||||
|             if (columnIndex == nColumns) { | ||||
|             if (columnIndex == nColumns) | ||||
|                 columnIndex = 0; | ||||
|             } | ||||
|  | ||||
|             if (columnIndex == 0) { | ||||
|                 y += this._getVItemSize() + spacing; | ||||
|                 if ((i + 1) % this._childrenPerPage == 0) | ||||
| @@ -955,15 +958,16 @@ var PaginatedIconGrid = GObject.registerClass({ | ||||
|  | ||||
|     /** | ||||
|     * openExtraSpace: | ||||
|     * @sourceItem: the item for which to create extra space | ||||
|     * @side: where @sourceItem should be located relative to the created space | ||||
|     * @nRows: the amount of space to create | ||||
|     * @param {Clutter.Actor} sourceItem: item for which to create extra space | ||||
|     * @param {St.Side} side: where @sourceItem should be located relative to | ||||
|     *   the created space | ||||
|     * @param {number} nRows: the amount of space to create | ||||
|     * | ||||
|     * Pan view to create extra space for @nRows above or below @sourceItem. | ||||
|     */ | ||||
|     openExtraSpace(sourceItem, side, nRows) { | ||||
|         let children = this._getVisibleChildren(); | ||||
|         let index = children.indexOf(sourceItem.actor); | ||||
|         let index = children.indexOf(sourceItem); | ||||
|         if (index == -1) | ||||
|             throw new Error('Item not found.'); | ||||
|  | ||||
| @@ -973,7 +977,7 @@ var PaginatedIconGrid = GObject.registerClass({ | ||||
|         let childrenPerRow = this._childrenPerPage / this._rowsPerPage; | ||||
|         let sourceRow = Math.floor((index - pageOffset) / childrenPerRow); | ||||
|  | ||||
|         let nRowsAbove = (side == St.Side.TOP) ? sourceRow + 1 : sourceRow; | ||||
|         let nRowsAbove = side == St.Side.TOP ? sourceRow + 1 : sourceRow; | ||||
|         let nRowsBelow = this._rowsPerPage - nRowsAbove; | ||||
|  | ||||
|         let nRowsUp, nRowsDown; | ||||
| @@ -1016,7 +1020,7 @@ var PaginatedIconGrid = GObject.registerClass({ | ||||
|             let params = { | ||||
|                 translation_y: translationY, | ||||
|                 duration: EXTRA_SPACE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, | ||||
|             }; | ||||
|             if (i == (children.length - 1)) | ||||
|                 params.onComplete = () => this.emit('space-opened'); | ||||
| @@ -1037,7 +1041,7 @@ var PaginatedIconGrid = GObject.registerClass({ | ||||
|                 translation_y: 0, | ||||
|                 duration: EXTRA_SPACE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, | ||||
|                 onComplete: () => this.emit('space-closed') | ||||
|                 onComplete: () => this.emit('space-closed'), | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -18,8 +18,8 @@ var DialogResponse = Meta.InhibitShortcutsDialogResponse; | ||||
| var InhibitShortcutsDialog = GObject.registerClass({ | ||||
|     Implements: [Meta.InhibitShortcutsDialog], | ||||
|     Properties: { | ||||
|         'window': GObject.ParamSpec.override('window', Meta.InhibitShortcutsDialog) | ||||
|     } | ||||
|         'window': GObject.ParamSpec.override('window', Meta.InhibitShortcutsDialog), | ||||
|     }, | ||||
| }, class InhibitShortcutsDialog extends GObject.Object { | ||||
|     _init(window) { | ||||
|         super._init(); | ||||
| @@ -84,10 +84,11 @@ var InhibitShortcutsDialog = GObject.registerClass({ | ||||
|         let contentParams = { icon, title }; | ||||
|  | ||||
|         let restoreAccel = this._getRestoreAccel(); | ||||
|         if (restoreAccel) | ||||
|         if (restoreAccel) { | ||||
|             contentParams.subtitle = | ||||
|                 /* Translators: %s is a keyboard shortcut like "Super+x" */ | ||||
|                 _("You can restore shortcuts by pressing %s.").format(restoreAccel); | ||||
|         } | ||||
|  | ||||
|         let content = new Dialog.MessageDialogContent(contentParams); | ||||
|         this._dialog.contentLayout.add_actor(content); | ||||
| @@ -134,10 +135,10 @@ var InhibitShortcutsDialog = GObject.registerClass({ | ||||
|  | ||||
|             this._permStore.LookupRemote(APP_PERMISSIONS_TABLE, | ||||
|                                          APP_PERMISSIONS_ID, | ||||
|                 (res, error) => { | ||||
|                     if (error) { | ||||
|                 (res, err) => { | ||||
|                     if (err) { | ||||
|                         this._dialog.open(); | ||||
|                         log(error.message); | ||||
|                         log(err.message); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|   | ||||
| @@ -61,7 +61,7 @@ class KbdA11yDialog extends GObject.Object { | ||||
|                                dialog.close(); | ||||
|                            }, | ||||
|                            default: enabled, | ||||
|                            key: !enabled ? Clutter.Escape : null }); | ||||
|                            key: !enabled ? Clutter.KEY_Escape : null }); | ||||
|  | ||||
|         dialog.addButton({ label: enabled ? _("Turn Off") : _("Leave Off"), | ||||
|                            action: () => { | ||||
| @@ -69,7 +69,7 @@ class KbdA11yDialog extends GObject.Object { | ||||
|                                dialog.close(); | ||||
|                            }, | ||||
|                            default: !enabled, | ||||
|                            key: enabled ? Clutter.Escape : null }); | ||||
|                            key: enabled ? Clutter.KEY_Escape : null }); | ||||
|  | ||||
|         dialog.open(); | ||||
|     } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										103
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								js/ui/layout.js
									
									
									
									
									
								
							| @@ -44,7 +44,7 @@ var MonitorConstraint = GObject.registerClass({ | ||||
|         'work-area': GObject.ParamSpec.boolean('work-area', | ||||
|                                                'Work-area', 'Track monitor\'s work-area', | ||||
|                                                GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, | ||||
|                                                false) | ||||
|                                                false), | ||||
|     }, | ||||
| }, class MonitorConstraint extends Clutter.Constraint { | ||||
|     _init(props) { | ||||
| @@ -167,12 +167,12 @@ var Monitor = class Monitor { | ||||
|  | ||||
| const UiActor = GObject.registerClass( | ||||
| class UiActor extends St.Widget { | ||||
|     vfunc_get_preferred_width (_forHeight) { | ||||
|     vfunc_get_preferred_width(_forHeight) { | ||||
|         let width = global.stage.width; | ||||
|         return [width, width]; | ||||
|     } | ||||
|  | ||||
|     vfunc_get_preferred_height (_forWidth) { | ||||
|     vfunc_get_preferred_height(_forWidth) { | ||||
|         let height = global.stage.height; | ||||
|         return [height, height]; | ||||
|     } | ||||
| @@ -181,7 +181,7 @@ class UiActor extends St.Widget { | ||||
| const defaultParams = { | ||||
|     trackFullscreen: false, | ||||
|     affectsStruts: false, | ||||
|     affectsInputRegion: true | ||||
|     affectsInputRegion: true, | ||||
| }; | ||||
|  | ||||
| var LayoutManager = GObject.registerClass({ | ||||
| @@ -189,12 +189,13 @@ var LayoutManager = GObject.registerClass({ | ||||
|                'startup-complete': {}, | ||||
|                'startup-prepared': {}, | ||||
|                'monitors-changed': {}, | ||||
|                'system-modal-opened': {}, | ||||
|                'keyboard-visible-changed': { param_types: [GObject.TYPE_BOOLEAN] } }, | ||||
| }, class LayoutManager extends GObject.Object { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|  | ||||
|         this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL); | ||||
|         this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; | ||||
|         this.monitors = []; | ||||
|         this.primaryMonitor = null; | ||||
|         this.primaryIndex = -1; | ||||
| @@ -212,11 +213,6 @@ var LayoutManager = GObject.registerClass({ | ||||
|         this._startingUp = true; | ||||
|         this._pendingLoadBackground = false; | ||||
|  | ||||
|         // We don't want to paint the stage background color because either | ||||
|         // the SystemBackground we create or the MetaBackgroundActor inside | ||||
|         // global.window_group covers the entirety of the screen. | ||||
|         global.stage.no_clear_hint = true; | ||||
|  | ||||
|         // Set up stage hierarchy to group all UI actors under one container. | ||||
|         this.uiGroup = new UiActor({ name: 'uiGroup' }); | ||||
|         this.uiGroup.set_flags(Clutter.ActorFlags.NO_LAYOUT); | ||||
| @@ -274,11 +270,11 @@ var LayoutManager = GObject.registerClass({ | ||||
|  | ||||
|         this._backgroundGroup = new Meta.BackgroundGroup(); | ||||
|         global.window_group.add_child(this._backgroundGroup); | ||||
|         this._backgroundGroup.lower_bottom(); | ||||
|         global.window_group.set_child_below_sibling(this._backgroundGroup, null); | ||||
|         this._bgManagers = []; | ||||
|  | ||||
|         this._interfaceSettings = new Gio.Settings({ | ||||
|             schema_id: 'org.gnome.desktop.interface' | ||||
|             schema_id: 'org.gnome.desktop.interface', | ||||
|         }); | ||||
|  | ||||
|         this._interfaceSettings.connect('changed::enable-hot-corners', | ||||
| @@ -344,10 +340,11 @@ var LayoutManager = GObject.registerClass({ | ||||
|  | ||||
|         this.monitors = []; | ||||
|         let nMonitors = display.get_n_monitors(); | ||||
|         for (let i = 0; i < nMonitors; i++) | ||||
|         for (let i = 0; i < nMonitors; i++) { | ||||
|             this.monitors.push(new Monitor(i, | ||||
|                                            display.get_monitor_geometry(i), | ||||
|                                            display.get_monitor_scale(i))); | ||||
|         } | ||||
|  | ||||
|         if (nMonitors == 0) { | ||||
|             this.primaryIndex = this.bottomIndex = -1; | ||||
| @@ -450,7 +447,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|     _createBackgroundManager(monitorIndex) { | ||||
|         let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup, | ||||
|                                                            layoutManager: this, | ||||
|                                                            monitorIndex: monitorIndex }); | ||||
|                                                            monitorIndex }); | ||||
|  | ||||
|         bgManager.connect('changed', this._addBackgroundMenu.bind(this)); | ||||
|         this._addBackgroundMenu(bgManager); | ||||
| @@ -467,15 +464,14 @@ var LayoutManager = GObject.registerClass({ | ||||
|                 backgroundActor.ease({ | ||||
|                     opacity: 255, | ||||
|                     duration: BACKGROUND_FADE_ANIMATION_TIME, | ||||
|                     mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                     mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _updateBackgrounds() { | ||||
|         let i; | ||||
|         for (i = 0; i < this._bgManagers.length; i++) | ||||
|         for (let i = 0; i < this._bgManagers.length; i++) | ||||
|             this._bgManagers[i].destroy(); | ||||
|  | ||||
|         this._bgManagers = []; | ||||
| @@ -606,17 +602,17 @@ var LayoutManager = GObject.registerClass({ | ||||
|             return; | ||||
|         } | ||||
|         this._systemBackground = new Background.SystemBackground(); | ||||
|         this._systemBackground.actor.hide(); | ||||
|         this._systemBackground.hide(); | ||||
|  | ||||
|         global.stage.insert_child_below(this._systemBackground.actor, null); | ||||
|         global.stage.insert_child_below(this._systemBackground, null); | ||||
|  | ||||
|         let constraint = new Clutter.BindConstraint({ source: global.stage, | ||||
|                                                       coordinate: Clutter.BindCoordinate.ALL }); | ||||
|         this._systemBackground.actor.add_constraint(constraint); | ||||
|         this._systemBackground.add_constraint(constraint); | ||||
|  | ||||
|         let signalId = this._systemBackground.connect('loaded', () => { | ||||
|             this._systemBackground.disconnect(signalId); | ||||
|             this._systemBackground.actor.show(); | ||||
|             this._systemBackground.show(); | ||||
|             global.stage.show(); | ||||
|  | ||||
|             this._prepareStartupAnimation(); | ||||
| @@ -703,7 +699,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|             translation_y: 0, | ||||
|             duration: STARTUP_ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this._startupAnimationComplete() | ||||
|             onComplete: () => this._startupAnimationComplete(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -714,7 +710,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|             opacity: 255, | ||||
|             duration: STARTUP_ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this._startupAnimationComplete() | ||||
|             onComplete: () => this._startupAnimationComplete(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -722,7 +718,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|         this._coverPane.destroy(); | ||||
|         this._coverPane = null; | ||||
|  | ||||
|         this._systemBackground.actor.destroy(); | ||||
|         this._systemBackground.destroy(); | ||||
|         this._systemBackground = null; | ||||
|  | ||||
|         this._startingUp = false; | ||||
| @@ -748,7 +744,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => { | ||||
|                 this._showKeyboardComplete(); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|         this.emit('keyboard-visible-changed', true); | ||||
|     } | ||||
| @@ -775,7 +771,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|             onComplete: () => { | ||||
|                 this._hideKeyboardComplete(); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|  | ||||
|         this.emit('keyboard-visible-changed', false); | ||||
| @@ -961,7 +957,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|     findIndexForActor(actor) { | ||||
|         let [x, y] = actor.get_transformed_position(); | ||||
|         let [w, h] = actor.get_transformed_size(); | ||||
|         let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h }); | ||||
|         let rect = new Meta.Rectangle({ x, y, width: w, height: h }); | ||||
|         return global.display.get_monitor_index_for_rect(rect); | ||||
|     } | ||||
|  | ||||
| @@ -976,9 +972,10 @@ var LayoutManager = GObject.registerClass({ | ||||
|         if (this._startingUp) | ||||
|             return; | ||||
|  | ||||
|         if (!this._updateRegionIdle) | ||||
|         if (!this._updateRegionIdle) { | ||||
|             this._updateRegionIdle = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, | ||||
|                                                     this._updateRegions.bind(this)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _getWindowActorsForWorkspace(workspace) { | ||||
| @@ -1032,7 +1029,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|             h = Math.round(h); | ||||
|  | ||||
|             if (actorData.affectsInputRegion && wantsInputRegion && actorData.actor.get_paint_visibility()) | ||||
|                 rects.push(new Meta.Rectangle({ x: x, y: y, width: w, height: h })); | ||||
|                 rects.push(new Meta.Rectangle({ x, y, width: w, height: h })); | ||||
|  | ||||
|             let monitor = null; | ||||
|             if (actorData.affectsStruts) | ||||
| @@ -1083,7 +1080,7 @@ var LayoutManager = GObject.registerClass({ | ||||
|                 } | ||||
|  | ||||
|                 let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1 }); | ||||
|                 let strut = new Meta.Strut({ rect: strutRect, side: side }); | ||||
|                 let strut = new Meta.Strut({ rect: strutRect, side }); | ||||
|                 struts.push(strut); | ||||
|             } | ||||
|         } | ||||
| @@ -1112,8 +1109,11 @@ var LayoutManager = GObject.registerClass({ | ||||
| // | ||||
| // This class manages a "hot corner" that can toggle switching to | ||||
| // overview. | ||||
| var HotCorner = class HotCorner { | ||||
|     constructor(layoutManager, monitor, x, y) { | ||||
| var HotCorner = GObject.registerClass( | ||||
| class HotCorner extends Clutter.Actor { | ||||
|     _init(layoutManager, monitor, x, y) { | ||||
|         super._init(); | ||||
|  | ||||
|         // We use this flag to mark the case where the user has entered the | ||||
|         // hot corner and has not left both the hot corner and a surrounding | ||||
|         // guard area (the "environs"). This avoids triggering the hot corner | ||||
| @@ -1142,6 +1142,8 @@ var HotCorner = class HotCorner { | ||||
|  | ||||
|         this._ripples = new Ripples.Ripples(px, py, 'ripple-box'); | ||||
|         this._ripples.addTo(layoutManager.uiGroup); | ||||
|  | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|     setBarrierSize(size) { | ||||
| @@ -1181,11 +1183,14 @@ var HotCorner = class HotCorner { | ||||
|  | ||||
|     _setupFallbackCornerIfNeeded(layoutManager) { | ||||
|         if (!global.display.supports_extended_barriers()) { | ||||
|             this.actor = new Clutter.Actor({ name: 'hot-corner-environs', | ||||
|                                              x: this._x, y: this._y, | ||||
|                                              width: 3, | ||||
|                                              height: 3, | ||||
|                                              reactive: true }); | ||||
|             this.set({ | ||||
|                 name: 'hot-corner-environs', | ||||
|                 x: this._x, | ||||
|                 y: this._y, | ||||
|                 width: 3, | ||||
|                 height: 3, | ||||
|                 reactive: true, | ||||
|             }); | ||||
|  | ||||
|             this._corner = new Clutter.Actor({ name: 'hot-corner', | ||||
|                                                width: 1, | ||||
| @@ -1194,19 +1199,16 @@ var HotCorner = class HotCorner { | ||||
|                                                reactive: true }); | ||||
|             this._corner._delegate = this; | ||||
|  | ||||
|             this.actor.add_child(this._corner); | ||||
|             layoutManager.addChrome(this.actor); | ||||
|             this.add_child(this._corner); | ||||
|             layoutManager.addChrome(this); | ||||
|  | ||||
|             if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) { | ||||
|                 this._corner.set_position(this.actor.width - this._corner.width, 0); | ||||
|                 this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); | ||||
|                 this._corner.set_position(this.width - this._corner.width, 0); | ||||
|                 this.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); | ||||
|             } else { | ||||
|                 this._corner.set_position(0, 0); | ||||
|             } | ||||
|  | ||||
|             this.actor.connect('leave-event', | ||||
|                                this._onEnvironsLeft.bind(this)); | ||||
|  | ||||
|             this._corner.connect('enter-event', | ||||
|                                  this._onCornerEntered.bind(this)); | ||||
|             this._corner.connect('leave-event', | ||||
| @@ -1214,14 +1216,11 @@ var HotCorner = class HotCorner { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|     _onDestroy() { | ||||
|         this.setBarrierSize(0); | ||||
|         this._pressureBarrier.destroy(); | ||||
|         this._pressureBarrier = null; | ||||
|  | ||||
|         if (this.actor) | ||||
|             this.actor.destroy(); | ||||
|  | ||||
|         this._ripples.destroy(); | ||||
|     } | ||||
|  | ||||
| @@ -1253,18 +1252,18 @@ var HotCorner = class HotCorner { | ||||
|     } | ||||
|  | ||||
|     _onCornerLeft(actor, event) { | ||||
|         if (event.get_related() != this.actor) | ||||
|         if (event.get_related() != this) | ||||
|             this._entered = false; | ||||
|         // Consume event, otherwise this will confuse onEnvironsLeft | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
|  | ||||
|     _onEnvironsLeft(actor, event) { | ||||
|         if (event.get_related() != this._corner) | ||||
|     vfunc_leave_event(crossingEvent) { | ||||
|         if (crossingEvent.related != this._corner) | ||||
|             this._entered = false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var PressureBarrier = class PressureBarrier { | ||||
|     constructor(threshold, timeout, actionMode) { | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
| /* exported Lightbox */ | ||||
|  | ||||
| const { Clutter, GObject, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| @@ -34,8 +33,8 @@ var RadialShaderEffect = GObject.registerClass({ | ||||
|             'sharpness', 'sharpness', 'sharpness', | ||||
|             GObject.ParamFlags.READWRITE, | ||||
|             0, 1, 0 | ||||
|         ) | ||||
|     } | ||||
|         ), | ||||
|     }, | ||||
| }, class RadialShaderEffect extends Shell.GLSLEffect { | ||||
|     _init(params) { | ||||
|         this._brightness = undefined; | ||||
| @@ -89,8 +88,8 @@ var RadialShaderEffect = GObject.registerClass({ | ||||
|  *           - inhibitEvents: whether to inhibit events for @container | ||||
|  *           - width: shade actor width | ||||
|  *           - height: shade actor height | ||||
|  *           - fadeInTime: milliseconds used to fade in | ||||
|  *           - fadeOutTime: milliseconds used to fade out | ||||
|  *           - fadeFactor: fading opacity factor | ||||
|  *           - radialEffect: whether to enable the GLSL radial effect | ||||
|  * | ||||
|  * Lightbox creates a dark translucent "shade" actor to hide the | ||||
|  * contents of @container, and allows you to specify particular actors | ||||
| @@ -106,8 +105,13 @@ var RadialShaderEffect = GObject.registerClass({ | ||||
|  * @container and will track any changes in its size. You can override | ||||
|  * this by passing an explicit width and height in @params. | ||||
|  */ | ||||
| var Lightbox = class Lightbox { | ||||
|     constructor(container, params) { | ||||
| var Lightbox = GObject.registerClass({ | ||||
|     Properties: { | ||||
|         'active': GObject.ParamSpec.boolean( | ||||
|             'active', 'active', 'active', GObject.ParamFlags.READABLE, false), | ||||
|     }, | ||||
| }, class Lightbox extends St.Bin { | ||||
|     _init(container, params) { | ||||
|         params = Params.parse(params, { | ||||
|             inhibitEvents: false, | ||||
|             width: null, | ||||
| @@ -116,32 +120,34 @@ var Lightbox = class Lightbox { | ||||
|             radialEffect: false, | ||||
|         }); | ||||
|  | ||||
|         super._init({ | ||||
|             reactive: params.inhibitEvents, | ||||
|             width: params.width, | ||||
|             height: params.height, | ||||
|             visible: false, | ||||
|         }); | ||||
|  | ||||
|         this._active = false; | ||||
|         this._container = container; | ||||
|         this._children = container.get_children(); | ||||
|         this._fadeFactor = params.fadeFactor; | ||||
|         this._radialEffect = Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL) && params.radialEffect; | ||||
|  | ||||
|         this.actor = new St.Bin({ reactive: params.inhibitEvents }); | ||||
|  | ||||
|         if (this._radialEffect) | ||||
|             this.actor.add_effect(new RadialShaderEffect({ name: 'radial' })); | ||||
|             this.add_effect(new RadialShaderEffect({ name: 'radial' })); | ||||
|         else | ||||
|             this.actor.set({ opacity: 0, style_class: 'lightbox' }); | ||||
|             this.set({ opacity: 0, style_class: 'lightbox' }); | ||||
|  | ||||
|         container.add_actor(this.actor); | ||||
|         this.actor.raise_top(); | ||||
|         this.actor.hide(); | ||||
|         this.shown = false; | ||||
|         container.add_actor(this); | ||||
|         container.set_child_above_sibling(this, null); | ||||
|  | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|  | ||||
|         if (params.width && params.height) { | ||||
|             this.actor.width = params.width; | ||||
|             this.actor.height = params.height; | ||||
|         } else { | ||||
|             let constraint = new Clutter.BindConstraint({ source: container, | ||||
|                                                           coordinate: Clutter.BindCoordinate.ALL }); | ||||
|             this.actor.add_constraint(constraint); | ||||
|         if (!params.width || !params.height) { | ||||
|             this.add_constraint(new Clutter.BindConstraint({ | ||||
|                 source: container, | ||||
|                 coordinate: Clutter.BindCoordinate.ALL, | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         this._actorAddedSignalId = container.connect('actor-added', this._actorAdded.bind(this)); | ||||
| @@ -150,16 +156,20 @@ var Lightbox = class Lightbox { | ||||
|         this._highlighted = null; | ||||
|     } | ||||
|  | ||||
|     get active() { | ||||
|         return this._active; | ||||
|     } | ||||
|  | ||||
|     _actorAdded(container, newChild) { | ||||
|         let children = this._container.get_children(); | ||||
|         let myIndex = children.indexOf(this.actor); | ||||
|         let myIndex = children.indexOf(this); | ||||
|         let newChildIndex = children.indexOf(newChild); | ||||
|  | ||||
|         if (newChildIndex > myIndex) { | ||||
|             // The child was added above the shade (presumably it was | ||||
|             // made the new top-most child). Move it below the shade, | ||||
|             // and add it to this._children as the new topmost actor. | ||||
|             newChild.lower(this.actor); | ||||
|             this._container.set_child_above_sibling(this, newChild); | ||||
|             this._children.push(newChild); | ||||
|         } else if (newChildIndex == 0) { | ||||
|             // Bottom of stack | ||||
| @@ -172,53 +182,55 @@ var Lightbox = class Lightbox { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     show(fadeInTime) { | ||||
|         this.actor.remove_all_transitions(); | ||||
|     lightOn(fadeInTime) { | ||||
|         this.remove_all_transitions(); | ||||
|  | ||||
|         let easeProps = { | ||||
|             duration: fadeInTime || 0, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }; | ||||
|  | ||||
|         let onComplete = () => { | ||||
|             this.shown = true; | ||||
|             this.emit('shown'); | ||||
|             this._active = true; | ||||
|             this.notify('active'); | ||||
|         }; | ||||
|  | ||||
|         this.actor.show(); | ||||
|         this.show(); | ||||
|  | ||||
|         if (this._radialEffect) { | ||||
|             this.actor.ease_property( | ||||
|             this.ease_property( | ||||
|                 '@effects.radial.brightness', VIGNETTE_BRIGHTNESS, easeProps); | ||||
|             this.actor.ease_property( | ||||
|             this.ease_property( | ||||
|                 '@effects.radial.sharpness', VIGNETTE_SHARPNESS, | ||||
|                 Object.assign({ onComplete }, easeProps)); | ||||
|         } else { | ||||
|             this.actor.ease(Object.assign(easeProps, { | ||||
|             this.ease(Object.assign(easeProps, { | ||||
|                 opacity: 255 * this._fadeFactor, | ||||
|                 onComplete | ||||
|                 onComplete, | ||||
|             })); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     hide(fadeOutTime) { | ||||
|         this.shown = false; | ||||
|         this.actor.remove_all_transitions(); | ||||
|     lightOff(fadeOutTime) { | ||||
|         this.remove_all_transitions(); | ||||
|  | ||||
|         this._active = false; | ||||
|         this.notify('active'); | ||||
|  | ||||
|         let easeProps = { | ||||
|             duration: fadeOutTime || 0, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }; | ||||
|  | ||||
|         let onComplete = () => this.actor.hide(); | ||||
|         let onComplete = () => this.hide(); | ||||
|  | ||||
|         if (this._radialEffect) { | ||||
|             this.actor.ease_property( | ||||
|             this.ease_property( | ||||
|                 '@effects.radial.brightness', 1.0, easeProps); | ||||
|             this.actor.ease_property( | ||||
|             this.ease_property( | ||||
|                 '@effects.radial.sharpness', 0.0, Object.assign({ onComplete }, easeProps)); | ||||
|         } else { | ||||
|             this.actor.ease(Object.assign(easeProps, { opacity: 0, onComplete })); | ||||
|             this.ease(Object.assign(easeProps, { opacity: 0, onComplete })); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -233,7 +245,7 @@ var Lightbox = class Lightbox { | ||||
|  | ||||
|     /** | ||||
|      * highlight: | ||||
|      * @window: actor to highlight | ||||
|      * @param {Clutter.Actor=} window: actor to highlight | ||||
|      * | ||||
|      * Highlights the indicated actor and unhighlights any other | ||||
|      * currently-highlighted actor. With no arguments or a false/null | ||||
| @@ -249,12 +261,12 @@ var Lightbox = class Lightbox { | ||||
|         // case we may need to indicate some *other* actor as the new | ||||
|         // sibling of the to-be-lowered one. | ||||
|  | ||||
|         let below = this.actor; | ||||
|         let below = this; | ||||
|         for (let i = this._children.length - 1; i >= 0; i--) { | ||||
|             if (this._children[i] == window) | ||||
|                 this._children[i].raise_top(); | ||||
|                 this._container.set_child_above_sibling(this._children[i], null); | ||||
|             else if (this._children[i] == this._highlighted) | ||||
|                 this._children[i].lower(below); | ||||
|                 this._container.set_child_below_sibling(this._children[i], below); | ||||
|             else | ||||
|                 below = this._children[i]; | ||||
|         } | ||||
| @@ -262,15 +274,6 @@ var Lightbox = class Lightbox { | ||||
|         this._highlighted = window; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * destroy: | ||||
|      * | ||||
|      * Destroys the lightbox. | ||||
|      */ | ||||
|     destroy() { | ||||
|         this.actor.destroy(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _onDestroy: | ||||
|      * | ||||
| @@ -278,10 +281,15 @@ var Lightbox = class Lightbox { | ||||
|      * by destroying its container or by explicitly calling this.destroy(). | ||||
|      */ | ||||
|     _onDestroy() { | ||||
|         this._container.disconnect(this._actorAddedSignalId); | ||||
|         this._container.disconnect(this._actorRemovedSignalId); | ||||
|         if (this._actorAddedSignalId) { | ||||
|             this._container.disconnect(this._actorAddedSignalId); | ||||
|             this._actorAddedSignalId = 0; | ||||
|         } | ||||
|         if (this._actorRemovedSignalId) { | ||||
|             this._container.disconnect(this._actorRemovedSignalId); | ||||
|             this._actorRemovedSignalId = 0; | ||||
|         } | ||||
|  | ||||
|         this.highlight(null); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Lightbox.prototype); | ||||
| }); | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported LookingGlass */ | ||||
|  | ||||
| const { Clutter, Cogl, Gio, GLib, | ||||
|         GObject, Meta, Pango, Shell, St } = imports.gi; | ||||
| const { Clutter, Cogl, Gio, GLib, GObject, | ||||
|         Graphene, Meta, Pango, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
| const System = imports.system; | ||||
|  | ||||
| @@ -54,9 +54,9 @@ var AutoComplete = class AutoComplete { | ||||
|     } | ||||
|  | ||||
|     _processCompletionRequest(event) { | ||||
|         if (event.completions.length == 0) { | ||||
|         if (event.completions.length == 0) | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Unique match = go ahead and complete; multiple matches + single tab = complete the common starting string; | ||||
|         // multiple matches + double tab = emit a suggest event with all possible options | ||||
|         if (event.completions.length == 1) { | ||||
| @@ -78,20 +78,20 @@ var AutoComplete = class AutoComplete { | ||||
|     _entryKeyPressEvent(actor, event) { | ||||
|         let cursorPos = this._entry.clutter_text.get_cursor_position(); | ||||
|         let text = this._entry.get_text(); | ||||
|         if (cursorPos != -1) { | ||||
|         if (cursorPos != -1) | ||||
|             text = text.slice(0, cursorPos); | ||||
|         } | ||||
|         if (event.get_key_symbol() == Clutter.Tab) { | ||||
|  | ||||
|         if (event.get_key_symbol() == Clutter.KEY_Tab) { | ||||
|             let [completions, attrHead] = JsParse.getCompletions(text, commandHeader, AUTO_COMPLETE_GLOBAL_KEYWORDS); | ||||
|             let currTime = global.get_current_time(); | ||||
|             if ((currTime - this._lastTabTime) < AUTO_COMPLETE_DOUBLE_TAB_DELAY) { | ||||
|                 this._processCompletionRequest({ tabType: 'double', | ||||
|                                                  completions: completions, | ||||
|                                                  attrHead: attrHead }); | ||||
|                                                  completions, | ||||
|                                                  attrHead }); | ||||
|             } else { | ||||
|                 this._processCompletionRequest({ tabType: 'single', | ||||
|                                                  completions: completions, | ||||
|                                                  attrHead: attrHead }); | ||||
|                                                  completions, | ||||
|                                                  attrHead }); | ||||
|             } | ||||
|             this._lastTabTime = currTime; | ||||
|         } | ||||
| @@ -110,9 +110,14 @@ var AutoComplete = class AutoComplete { | ||||
| Signals.addSignalMethods(AutoComplete.prototype); | ||||
|  | ||||
|  | ||||
| var Notebook = class Notebook { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ vertical: true }); | ||||
| var Notebook = GObject.registerClass({ | ||||
|     Signals: { 'selection': { param_types: [Clutter.Actor.$gtype] } }, | ||||
| }, class Notebook extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             vertical: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|  | ||||
|         this.tabControls = new St.BoxLayout({ style_class: 'labels' }); | ||||
|  | ||||
| @@ -129,21 +134,21 @@ var Notebook = class Notebook { | ||||
|             this.selectChild(child); | ||||
|             return true; | ||||
|         }); | ||||
|         labelBox.add(label, { expand: true }); | ||||
|         labelBox.add_child(label); | ||||
|         this.tabControls.add(labelBox); | ||||
|  | ||||
|         let scrollview = new St.ScrollView({ x_fill: true, y_fill: true }); | ||||
|         let scrollview = new St.ScrollView({ y_expand: true }); | ||||
|         scrollview.get_hscroll_bar().hide(); | ||||
|         scrollview.add_actor(child); | ||||
|  | ||||
|         let tabData = { child: child, | ||||
|                         labelBox: labelBox, | ||||
|                         label: label, | ||||
|         let tabData = { child, | ||||
|                         labelBox, | ||||
|                         label, | ||||
|                         scrollView: scrollview, | ||||
|                         _scrollToBottom: false }; | ||||
|         this._tabs.push(tabData); | ||||
|         scrollview.hide(); | ||||
|         this.actor.add(scrollview, { expand: true }); | ||||
|         this.add_child(scrollview); | ||||
|  | ||||
|         let vAdjust = scrollview.vscroll.adjustment; | ||||
|         vAdjust.connect('changed', () => this._onAdjustScopeChanged(tabData)); | ||||
| @@ -174,7 +179,7 @@ var Notebook = class Notebook { | ||||
|         // Focus the new tab before unmapping the old one | ||||
|         let tabData = this._tabs[index]; | ||||
|         if (!tabData.scrollView.navigate_focus(null, St.DirectionType.TAB_FORWARD, false)) | ||||
|             this.actor.grab_key_focus(); | ||||
|             this.grab_key_focus(); | ||||
|  | ||||
|         this._unselect(); | ||||
|  | ||||
| @@ -219,23 +224,20 @@ var Notebook = class Notebook { | ||||
|  | ||||
|     nextTab() { | ||||
|         let nextIndex = this._selectedIndex; | ||||
|         if (nextIndex < this._tabs.length - 1) { | ||||
|         if (nextIndex < this._tabs.length - 1) | ||||
|             ++nextIndex; | ||||
|         } | ||||
|  | ||||
|         this.selectIndex(nextIndex); | ||||
|     } | ||||
|  | ||||
|     prevTab() { | ||||
|         let prevIndex = this._selectedIndex; | ||||
|         if (prevIndex > 0) { | ||||
|         if (prevIndex > 0) | ||||
|             --prevIndex; | ||||
|         } | ||||
|  | ||||
|         this.selectIndex(prevIndex); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Notebook.prototype); | ||||
| }); | ||||
|  | ||||
| function objectToString(o) { | ||||
|     if (typeof o == typeof objectToString) { | ||||
| @@ -246,57 +248,63 @@ function objectToString(o) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| var ObjLink = class ObjLink { | ||||
|     constructor(lookingGlass, o, title) { | ||||
| var ObjLink = GObject.registerClass( | ||||
| class ObjLink extends St.Button { | ||||
|     _init(lookingGlass, o, title) { | ||||
|         let text; | ||||
|         if (title) | ||||
|             text = title; | ||||
|         else | ||||
|             text = objectToString(o); | ||||
|         text = GLib.markup_escape_text(text, -1); | ||||
|  | ||||
|         super._init({ | ||||
|             reactive: true, | ||||
|             track_hover: true, | ||||
|             style_class: 'shell-link', | ||||
|             label: text, | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|         this.get_child().single_line_mode = true; | ||||
|  | ||||
|         this._obj = o; | ||||
|  | ||||
|         this.actor = new St.Button({ reactive: true, | ||||
|                                      track_hover: true, | ||||
|                                      style_class: 'shell-link', | ||||
|                                      label: text }); | ||||
|         this.actor.get_child().single_line_mode = true; | ||||
|         this.actor.connect('clicked', this._onClicked.bind(this)); | ||||
|  | ||||
|         this._lookingGlass = lookingGlass; | ||||
|     } | ||||
|  | ||||
|     _onClicked() { | ||||
|         this._lookingGlass.inspectObject(this._obj, this.actor); | ||||
|     vfunc_clicked() { | ||||
|         this._lookingGlass.inspectObject(this._obj, this); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var Result = GObject.registerClass( | ||||
| class Result extends St.BoxLayout { | ||||
|     _init(lookingGlass, command, o, index) { | ||||
|         super._init({ vertical: true }); | ||||
|  | ||||
| var Result = class Result { | ||||
|     constructor(lookingGlass, command, o, index) { | ||||
|         this.index = index; | ||||
|         this.o = o; | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ vertical: true }); | ||||
|         this._lookingGlass = lookingGlass; | ||||
|  | ||||
|         let cmdTxt = new St.Label({ text: command }); | ||||
|         cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END; | ||||
|         this.actor.add(cmdTxt); | ||||
|         this.add(cmdTxt); | ||||
|         let box = new St.BoxLayout({}); | ||||
|         this.actor.add(box); | ||||
|         this.add(box); | ||||
|         let resultTxt = new St.Label({ text: `r(${index}) = ` }); | ||||
|         resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END; | ||||
|         box.add(resultTxt); | ||||
|         let objLink = new ObjLink(this._lookingGlass, o); | ||||
|         box.add(objLink.actor); | ||||
|         box.add(objLink); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var WindowList = class WindowList { | ||||
|     constructor(lookingGlass) { | ||||
|         this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' }); | ||||
| var WindowList = GObject.registerClass({ | ||||
| }, class WindowList extends St.BoxLayout { | ||||
|     _init(lookingGlass) { | ||||
|         super._init({ name: 'Windows', vertical: true, style: 'spacing: 8px' }); | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         this._updateId = Main.initializeDeferredWork(this.actor, this._updateWindowList.bind(this)); | ||||
|         this._updateId = Main.initializeDeferredWork(this, this._updateWindowList.bind(this)); | ||||
|         global.display.connect('window-created', this._updateWindowList.bind(this)); | ||||
|         tracker.connect('tracked-windows-changed', this._updateWindowList.bind(this)); | ||||
|  | ||||
| @@ -307,7 +315,7 @@ var WindowList = class WindowList { | ||||
|         if (!this._lookingGlass.isOpen) | ||||
|             return; | ||||
|  | ||||
|         this.actor.destroy_all_children(); | ||||
|         this.destroy_all_children(); | ||||
|         let windows = global.get_window_actors(); | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         for (let i = 0; i < windows.length; i++) { | ||||
| @@ -318,9 +326,9 @@ var WindowList = class WindowList { | ||||
|                 metaWindow._lookingGlassManaged = true; | ||||
|             } | ||||
|             let box = new St.BoxLayout({ vertical: true }); | ||||
|             this.actor.add(box); | ||||
|             this.add(box); | ||||
|             let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title); | ||||
|             box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false }); | ||||
|             box.add_child(windowLink); | ||||
|             let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' }); | ||||
|             box.add(propsBox); | ||||
|             propsBox.add(new St.Label({ text: `wmclass: ${metaWindow.get_wm_class()}` })); | ||||
| @@ -329,10 +337,10 @@ var WindowList = class WindowList { | ||||
|                 let icon = app.create_icon_texture(22); | ||||
|                 let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' }); | ||||
|                 propsBox.add(propBox); | ||||
|                 propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false }); | ||||
|                 propBox.add_child(new St.Label({ text: 'app: ' })); | ||||
|                 let appLink = new ObjLink(this._lookingGlass, app, app.get_id()); | ||||
|                 propBox.add(appLink.actor, { y_fill: false }); | ||||
|                 propBox.add(icon, { y_fill: false }); | ||||
|                 propBox.add_child(appLink); | ||||
|                 propBox.add_child(icon); | ||||
|             } else { | ||||
|                 propsBox.add(new St.Label({ text: '<untracked>' })); | ||||
|             } | ||||
| @@ -342,23 +350,29 @@ var WindowList = class WindowList { | ||||
|     update() { | ||||
|         this._updateWindowList(); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(WindowList.prototype); | ||||
| }); | ||||
|  | ||||
| var ObjInspector = GObject.registerClass( | ||||
| class ObjInspector extends St.ScrollView { | ||||
|     _init(lookingGlass) { | ||||
|         super._init({ | ||||
|             pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), | ||||
|         }); | ||||
|  | ||||
| var ObjInspector = class ObjInspector { | ||||
|     constructor(lookingGlass) { | ||||
|         this._obj = null; | ||||
|         this._previousObj = null; | ||||
|  | ||||
|         this._parentList = []; | ||||
|  | ||||
|         this.actor = new St.ScrollView({ pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }), | ||||
|                                          x_fill: true, y_fill: true }); | ||||
|         this.actor.get_hscroll_bar().hide(); | ||||
|         this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector', | ||||
|                                              style_class: 'lg-dialog', | ||||
|                                              vertical: true }); | ||||
|         this.actor.add_actor(this._container); | ||||
|         this.get_hscroll_bar().hide(); | ||||
|         this._container = new St.BoxLayout({ | ||||
|             name: 'LookingGlassPropertyInspector', | ||||
|             style_class: 'lg-dialog', | ||||
|             vertical: true, | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this.add_actor(this._container); | ||||
|  | ||||
|         this._lookingGlass = lookingGlass; | ||||
|     } | ||||
| @@ -374,10 +388,12 @@ var ObjInspector = class ObjInspector { | ||||
|  | ||||
|         let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' }); | ||||
|         this._container.add_actor(hbox); | ||||
|         let label = new St.Label({ text: 'Inspecting: %s: %s'.format(typeof obj, | ||||
|                                                                      objectToString(obj)) }); | ||||
|         let label = new St.Label({ | ||||
|             text: 'Inspecting: %s: %s'.format(typeof obj, objectToString(obj)), | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         label.single_line_mode = true; | ||||
|         hbox.add(label, { expand: true, y_fill: false }); | ||||
|         hbox.add_child(label); | ||||
|         let button = new St.Button({ label: 'Insert', style_class: 'lg-obj-inspector-button' }); | ||||
|         button.connect('clicked', this._onInsert.bind(this)); | ||||
|         hbox.add(button); | ||||
| @@ -394,9 +410,8 @@ var ObjInspector = class ObjInspector { | ||||
|         hbox.add(button); | ||||
|         if (typeof obj == typeof {}) { | ||||
|             let properties = []; | ||||
|             for (let propName in obj) { | ||||
|             for (let propName in obj) | ||||
|                 properties.push(propName); | ||||
|             } | ||||
|             properties.sort(); | ||||
|  | ||||
|             for (let i = 0; i < properties.length; i++) { | ||||
| @@ -404,14 +419,14 @@ var ObjInspector = class ObjInspector { | ||||
|                 let link; | ||||
|                 try { | ||||
|                     let prop = obj[propName]; | ||||
|                     link = new ObjLink(this._lookingGlass, prop).actor; | ||||
|                     link = new ObjLink(this._lookingGlass, prop); | ||||
|                 } catch (e) { | ||||
|                     link = new St.Label({ text: '<error>' }); | ||||
|                 } | ||||
|                 let hbox = new St.BoxLayout(); | ||||
|                 hbox.add(new St.Label({ text: `${propName}: ` })); | ||||
|                 hbox.add(link); | ||||
|                 this._container.add_actor(hbox); | ||||
|                 let box = new St.BoxLayout(); | ||||
|                 box.add(new St.Label({ text: `${propName}: ` })); | ||||
|                 box.add(link); | ||||
|                 this._container.add_actor(box); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -421,17 +436,17 @@ var ObjInspector = class ObjInspector { | ||||
|             return; | ||||
|         this._previousObj = null; | ||||
|         this._open = true; | ||||
|         this.actor.show(); | ||||
|         this.show(); | ||||
|         if (sourceActor) { | ||||
|             this.actor.set_scale(0, 0); | ||||
|             this.actor.ease({ | ||||
|             this.set_scale(0, 0); | ||||
|             this.ease({ | ||||
|                 scale_x: 1, | ||||
|                 scale_y: 1, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 duration: 200 | ||||
|                 duration: 200, | ||||
|             }); | ||||
|         } else { | ||||
|             this.actor.set_scale(1, 1); | ||||
|             this.set_scale(1, 1); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -439,7 +454,7 @@ var ObjInspector = class ObjInspector { | ||||
|         if (!this._open) | ||||
|             return; | ||||
|         this._open = false; | ||||
|         this.actor.hide(); | ||||
|         this.hide(); | ||||
|         this._previousObj = null; | ||||
|         this._obj = null; | ||||
|     } | ||||
| @@ -453,29 +468,44 @@ var ObjInspector = class ObjInspector { | ||||
|     _onBack() { | ||||
|         this.selectObject(this._previousObj, true); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var RedBorderEffect = GObject.registerClass( | ||||
| class RedBorderEffect extends Clutter.Effect { | ||||
|     vfunc_paint() { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|         this._pipeline = null; | ||||
|     } | ||||
|  | ||||
|     vfunc_paint(paintContext) { | ||||
|         let framebuffer = paintContext.get_framebuffer(); | ||||
|         let coglContext = framebuffer.get_context(); | ||||
|         let actor = this.get_actor(); | ||||
|         actor.continue_paint(); | ||||
|         actor.continue_paint(paintContext); | ||||
|  | ||||
|         let color = new Cogl.Color(); | ||||
|         color.init_from_4ub(0xff, 0, 0, 0xc4); | ||||
|         Cogl.set_source_color(color); | ||||
|         if (!this._pipeline) { | ||||
|             let color = new Cogl.Color(); | ||||
|             color.init_from_4ub(0xff, 0, 0, 0xc4); | ||||
|  | ||||
|         let geom = actor.get_allocation_geometry(); | ||||
|             this._pipeline = new Cogl.Pipeline(coglContext); | ||||
|             this._pipeline.set_color(color); | ||||
|         } | ||||
|  | ||||
|         let alloc = actor.get_allocation_box(); | ||||
|         let width = 2; | ||||
|  | ||||
|         // clockwise order | ||||
|         Cogl.rectangle(0, 0, geom.width, width); | ||||
|         Cogl.rectangle(geom.width - width, width, | ||||
|                        geom.width, geom.height); | ||||
|         Cogl.rectangle(0, geom.height, | ||||
|                        geom.width - width, geom.height - width); | ||||
|         Cogl.rectangle(0, geom.height - width, | ||||
|                        width, width); | ||||
|         framebuffer.draw_rectangle(this._pipeline, | ||||
|             0, 0, alloc.get_width(), width); | ||||
|         framebuffer.draw_rectangle(this._pipeline, | ||||
|             alloc.get_width() - width, width, | ||||
|             alloc.get_width(), alloc.get_height()); | ||||
|         framebuffer.draw_rectangle(this._pipeline, | ||||
|             0, alloc.get_height(), | ||||
|             alloc.get_width() - width, alloc.get_height() - width); | ||||
|         framebuffer.draw_rectangle(this._pipeline, | ||||
|             0, alloc.get_height() - width, | ||||
|             width, width); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -484,8 +514,7 @@ var Inspector = GObject.registerClass({ | ||||
|                'target': { param_types: [Clutter.Actor.$gtype, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] } }, | ||||
| }, class Inspector extends Clutter.Actor { | ||||
|     _init(lookingGlass) { | ||||
|         super._init({ width: 0, | ||||
|                       height: 0 }); | ||||
|         super._init({ width: 0, height: 0 }); | ||||
|  | ||||
|         Main.uiGroup.add_actor(this); | ||||
|  | ||||
| @@ -494,8 +523,8 @@ var Inspector = GObject.registerClass({ | ||||
|                                               reactive: true }); | ||||
|         this._eventHandler = eventHandler; | ||||
|         this.add_actor(eventHandler); | ||||
|         this._displayText = new St.Label(); | ||||
|         eventHandler.add(this._displayText, { expand: true }); | ||||
|         this._displayText = new St.Label({ x_expand: true }); | ||||
|         eventHandler.add_child(this._displayText); | ||||
|  | ||||
|         eventHandler.connect('key-press-event', this._onKeyPressEvent.bind(this)); | ||||
|         eventHandler.connect('button-press-event', this._onButtonPressEvent.bind(this)); | ||||
| @@ -548,7 +577,7 @@ var Inspector = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     _onKeyPressEvent(actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.Escape) | ||||
|         if (event.get_key_symbol() === Clutter.KEY_Escape) | ||||
|             this._close(); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| @@ -620,18 +649,19 @@ var Inspector = GObject.registerClass({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var Extensions = class Extensions { | ||||
|     constructor(lookingGlass) { | ||||
| var Extensions = GObject.registerClass({ | ||||
| }, class Extensions extends St.BoxLayout { | ||||
|     _init(lookingGlass) { | ||||
|         super._init({ vertical: true, name: 'lookingGlassExtensions' }); | ||||
|  | ||||
|         this._lookingGlass = lookingGlass; | ||||
|         this.actor = new St.BoxLayout({ vertical: true, | ||||
|                                         name: 'lookingGlassExtensions' }); | ||||
|         this._noExtensions = new St.Label({ style_class: 'lg-extensions-none', | ||||
|                                             text: _("No extensions installed") }); | ||||
|         this._numExtensions = 0; | ||||
|         this._extensionsList = new St.BoxLayout({ vertical: true, | ||||
|                                                   style_class: 'lg-extensions-list' }); | ||||
|         this._extensionsList.add(this._noExtensions); | ||||
|         this.actor.add(this._extensionsList); | ||||
|         this.add(this._extensionsList); | ||||
|  | ||||
|         Main.extensionManager.getUuids().forEach(uuid => { | ||||
|             this._loadExtension(null, uuid); | ||||
| @@ -652,7 +682,7 @@ var Extensions = class Extensions { | ||||
|         if (this._numExtensions == 0) | ||||
|             this._extensionsList.remove_actor(this._noExtensions); | ||||
|  | ||||
|         this._numExtensions ++; | ||||
|         this._numExtensions++; | ||||
|         this._extensionsList.add(extensionDisplay); | ||||
|     } | ||||
|  | ||||
| @@ -677,7 +707,7 @@ var Extensions = class Extensions { | ||||
|             let errors = extension.errors; | ||||
|             let errorDisplay = new St.BoxLayout({ vertical: true }); | ||||
|             if (errors && errors.length) { | ||||
|                 for (let i = 0; i < errors.length; i ++) | ||||
|                 for (let i = 0; i < errors.length; i++) | ||||
|                     errorDisplay.add(new St.Label({ text: errors[i] })); | ||||
|             } else { | ||||
|                 /* Translators: argument is an extension UUID. */ | ||||
| @@ -716,12 +746,18 @@ var Extensions = class Extensions { | ||||
|  | ||||
|     _createExtensionDisplay(extension) { | ||||
|         let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true }); | ||||
|         let name = new St.Label({ style_class: 'lg-extension-name', | ||||
|                                   text: extension.metadata.name }); | ||||
|         box.add(name, { expand: true }); | ||||
|         let description = new St.Label({ style_class: 'lg-extension-description', | ||||
|                                          text: extension.metadata.description || 'No description' }); | ||||
|         box.add(description, { expand: true }); | ||||
|         let name = new St.Label({ | ||||
|             style_class: 'lg-extension-name', | ||||
|             text: extension.metadata.name, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         box.add_child(name); | ||||
|         let description = new St.Label({ | ||||
|             style_class: 'lg-extension-description', | ||||
|             text: extension.metadata.description || 'No description', | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         box.add_child(description); | ||||
|  | ||||
|         let metaBox = new St.BoxLayout({ style_class: 'lg-extension-meta' }); | ||||
|         box.add(metaBox); | ||||
| @@ -759,10 +795,19 @@ var Extensions = class Extensions { | ||||
|  | ||||
|         return box; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var LookingGlass = GObject.registerClass( | ||||
| class LookingGlass extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             name: 'LookingGlassDialog', | ||||
|             style_class: 'lg-dialog', | ||||
|             vertical: true, | ||||
|             visible: false, | ||||
|             reactive: true, | ||||
|         }); | ||||
|  | ||||
| var LookingGlass = class LookingGlass { | ||||
|     constructor() { | ||||
|         this._borderPaintTarget = null; | ||||
|         this._redBorderEffect = new RedBorderEffect(); | ||||
|  | ||||
| @@ -770,26 +815,18 @@ var LookingGlass = class LookingGlass { | ||||
|  | ||||
|         this._it = null; | ||||
|         this._offset = 0; | ||||
|         this._results = []; | ||||
|  | ||||
|         // Sort of magic, but...eh. | ||||
|         this._maxItems = 150; | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ name: 'LookingGlassDialog', | ||||
|                                         style_class: 'lg-dialog', | ||||
|                                         vertical: true, | ||||
|                                         visible: false, | ||||
|                                         reactive: true }); | ||||
|         this.actor.connect('key-press-event', this._globalKeyPressEvent.bind(this)); | ||||
|  | ||||
|         this._interfaceSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' }); | ||||
|         this._interfaceSettings.connect('changed::monospace-font-name', | ||||
|                                         this._updateFont.bind(this)); | ||||
|         this._updateFont(); | ||||
|  | ||||
|         // We want it to appear to slide out from underneath the panel | ||||
|         Main.uiGroup.add_actor(this.actor); | ||||
|         Main.uiGroup.set_child_below_sibling(this.actor, | ||||
|         Main.uiGroup.add_actor(this); | ||||
|         Main.uiGroup.set_child_below_sibling(this, | ||||
|                                              Main.layoutManager.panelBox); | ||||
|         Main.layoutManager.panelBox.connect('allocation-changed', | ||||
|                                             this._queueResize.bind(this)); | ||||
| @@ -797,11 +834,11 @@ var LookingGlass = class LookingGlass { | ||||
|                                                this._queueResize.bind(this)); | ||||
|  | ||||
|         this._objInspector = new ObjInspector(this); | ||||
|         Main.uiGroup.add_actor(this._objInspector.actor); | ||||
|         this._objInspector.actor.hide(); | ||||
|         Main.uiGroup.add_actor(this._objInspector); | ||||
|         this._objInspector.hide(); | ||||
|  | ||||
|         let toolbar = new St.BoxLayout({ name: 'Toolbar' }); | ||||
|         this.actor.add_actor(toolbar); | ||||
|         this.add_actor(toolbar); | ||||
|         let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker', | ||||
|                                         icon_size: 24 }); | ||||
|         toolbar.add_actor(inspectIcon); | ||||
| @@ -812,10 +849,10 @@ var LookingGlass = class LookingGlass { | ||||
|                 this._pushResult(`inspect(${Math.round(stageX)}, ${Math.round(stageY)})`, target); | ||||
|             }); | ||||
|             inspector.connect('closed', () => { | ||||
|                 this.actor.show(); | ||||
|                 this.show(); | ||||
|                 global.stage.set_key_focus(this._entry); | ||||
|             }); | ||||
|             this.actor.hide(); | ||||
|             this.hide(); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         }); | ||||
|  | ||||
| @@ -837,33 +874,43 @@ var LookingGlass = class LookingGlass { | ||||
|  | ||||
|         let notebook = new Notebook(); | ||||
|         this._notebook = notebook; | ||||
|         this.actor.add(notebook.actor, { expand: true }); | ||||
|         this.add_child(notebook); | ||||
|  | ||||
|         let emptyBox = new St.Bin(); | ||||
|         toolbar.add(emptyBox, { expand: true }); | ||||
|         let emptyBox = new St.Bin({ x_expand: true }); | ||||
|         toolbar.add_child(emptyBox); | ||||
|         toolbar.add_actor(notebook.tabControls); | ||||
|  | ||||
|         this._evalBox = new St.BoxLayout({ name: 'EvalBox', vertical: true }); | ||||
|         notebook.appendPage('Evaluator', this._evalBox); | ||||
|  | ||||
|         this._resultsArea = new St.BoxLayout({ name: 'ResultsArea', vertical: true }); | ||||
|         this._evalBox.add(this._resultsArea, { expand: true }); | ||||
|         this._resultsArea = new St.BoxLayout({ | ||||
|             name: 'ResultsArea', | ||||
|             vertical: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this._evalBox.add_child(this._resultsArea); | ||||
|  | ||||
|         this._entryArea = new St.BoxLayout({ name: 'EntryArea' }); | ||||
|         this._entryArea = new St.BoxLayout({ | ||||
|             name: 'EntryArea', | ||||
|             y_align: Clutter.ActorAlign.END, | ||||
|         }); | ||||
|         this._evalBox.add_actor(this._entryArea); | ||||
|  | ||||
|         let label = new St.Label({ text: CHEVRON }); | ||||
|         this._entryArea.add(label); | ||||
|  | ||||
|         this._entry = new St.Entry({ can_focus: true }); | ||||
|         this._entry = new St.Entry({ | ||||
|             can_focus: true, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         ShellEntry.addContextMenu(this._entry); | ||||
|         this._entryArea.add(this._entry, { expand: true }); | ||||
|         this._entryArea.add_child(this._entry); | ||||
|  | ||||
|         this._windowList = new WindowList(this); | ||||
|         notebook.appendPage('Windows', this._windowList.actor); | ||||
|         notebook.appendPage('Windows', this._windowList); | ||||
|  | ||||
|         this._extensions = new Extensions(this); | ||||
|         notebook.appendPage('Extensions', this._extensions.actor); | ||||
|         notebook.appendPage('Extensions', this._extensions); | ||||
|  | ||||
|         this._entry.clutter_text.connect('activate', (o, _e) => { | ||||
|             // Hide any completions we are currently showing | ||||
| @@ -905,7 +952,7 @@ var LookingGlass = class LookingGlass { | ||||
|         // monospace font to be bold/oblique/etc. Could easily be added here. | ||||
|         let size = fontDesc.get_size() / 1024.; | ||||
|         let unit = fontDesc.get_size_is_absolute() ? 'px' : 'pt'; | ||||
|         this.actor.style = ` | ||||
|         this.style = ` | ||||
|             font-size: ${size}${unit}; | ||||
|             font-family: "${fontDesc.get_family()}";`; | ||||
|     } | ||||
| @@ -919,17 +966,14 @@ var LookingGlass = class LookingGlass { | ||||
|     } | ||||
|  | ||||
|     _pushResult(command, obj) { | ||||
|         let index = this._results.length + this._offset; | ||||
|         let index = this._resultsArea.get_n_children() + this._offset; | ||||
|         let result = new Result(this, CHEVRON + command, obj, index); | ||||
|         this._results.push(result); | ||||
|         this._resultsArea.add(result.actor); | ||||
|         this._resultsArea.add(result); | ||||
|         if (obj instanceof Clutter.Actor) | ||||
|             this.setBorderPaintTarget(obj); | ||||
|  | ||||
|         let children = this._resultsArea.get_children(); | ||||
|         if (children.length > this._maxItems) { | ||||
|             this._results.shift(); | ||||
|             children[0].destroy(); | ||||
|         if (this._resultsArea.get_n_children() > this._maxItems) { | ||||
|             this._resultsArea.get_first_child().destroy(); | ||||
|             this._offset++; | ||||
|         } | ||||
|         this._it = obj; | ||||
| @@ -965,7 +1009,7 @@ var LookingGlass = class LookingGlass { | ||||
|                 height: naturalHeight, | ||||
|                 opacity: 255, | ||||
|                 duration, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -982,7 +1026,7 @@ var LookingGlass = class LookingGlass { | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 onComplete: () => { | ||||
|                     this._completionActor.hide(); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -1016,7 +1060,7 @@ var LookingGlass = class LookingGlass { | ||||
|  | ||||
|     getResult(idx) { | ||||
|         try { | ||||
|             return this._results[idx - this._offset].o; | ||||
|             return this._resultsArea.get_child_at_index(idx - this._offset).o; | ||||
|         } catch (e) { | ||||
|             throw new Error(`Unknown result at index ${idx}`); | ||||
|         } | ||||
| @@ -1041,15 +1085,15 @@ var LookingGlass = class LookingGlass { | ||||
|         let myWidth = primary.width * 0.7; | ||||
|         let availableHeight = primary.height - Main.layoutManager.keyboardBox.height; | ||||
|         let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9); | ||||
|         this.actor.x = primary.x + (primary.width - myWidth) / 2; | ||||
|         this.x = primary.x + (primary.width - myWidth) / 2; | ||||
|         this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight; | ||||
|         this._targetY = this._hiddenY + myHeight; | ||||
|         this.actor.y = this._hiddenY; | ||||
|         this.actor.width = myWidth; | ||||
|         this.actor.height = myHeight; | ||||
|         this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8)); | ||||
|         this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1), | ||||
|                                               this._targetY + Math.floor(myHeight * 0.1)); | ||||
|         this.y = this._hiddenY; | ||||
|         this.width = myWidth; | ||||
|         this.height = myHeight; | ||||
|         this._objInspector.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8)); | ||||
|         this._objInspector.set_position(this.x + Math.floor(myWidth * 0.1), | ||||
|                                         this._targetY + Math.floor(myHeight * 0.1)); | ||||
|     } | ||||
|  | ||||
|     insertObject(obj) { | ||||
| @@ -1062,24 +1106,21 @@ var LookingGlass = class LookingGlass { | ||||
|     } | ||||
|  | ||||
|     // Handle key events which are relevant for all tabs of the LookingGlass | ||||
|     _globalKeyPressEvent(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         let modifierState = event.get_state(); | ||||
|         if (symbol == Clutter.Escape) { | ||||
|             if (this._objInspector.actor.visible) { | ||||
|     vfunc_key_press_event(keyPressEvent) { | ||||
|         let symbol = keyPressEvent.keyval; | ||||
|         if (symbol == Clutter.KEY_Escape) { | ||||
|             if (this._objInspector.visible) | ||||
|                 this._objInspector.close(); | ||||
|             } else { | ||||
|             else | ||||
|                 this.close(); | ||||
|             } | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         // Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view | ||||
|         if (modifierState & Clutter.ModifierType.CONTROL_MASK) { | ||||
|             if (symbol == Clutter.KEY_Page_Up) { | ||||
|         if (keyPressEvent.modifier_state & Clutter.ModifierType.CONTROL_MASK) { | ||||
|             if (symbol == Clutter.KEY_Page_Up) | ||||
|                 this._notebook.prevTab(); | ||||
|             } else if (symbol == Clutter.KEY_Page_Down) { | ||||
|             else if (symbol == Clutter.KEY_Page_Down) | ||||
|                 this._notebook.nextTab(); | ||||
|             } | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| @@ -1092,19 +1133,19 @@ var LookingGlass = class LookingGlass { | ||||
|             return; | ||||
|  | ||||
|         this._notebook.selectIndex(0); | ||||
|         this.actor.show(); | ||||
|         this.show(); | ||||
|         this._open = true; | ||||
|         this._history.lastItem(); | ||||
|  | ||||
|         this.actor.remove_all_transitions(); | ||||
|         this.remove_all_transitions(); | ||||
|  | ||||
|         // We inverse compensate for the slow-down so you can change the factor | ||||
|         // through LookingGlass without long waits. | ||||
|         let duration = LG_ANIMATION_TIME / St.Settings.get().slow_down_factor; | ||||
|         this.actor.ease({ | ||||
|         this.ease({ | ||||
|             y: this._targetY, | ||||
|             duration, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|  | ||||
|         this._windowList.update(); | ||||
| @@ -1114,10 +1155,10 @@ var LookingGlass = class LookingGlass { | ||||
|         if (!this._open) | ||||
|             return; | ||||
|  | ||||
|         this._objInspector.actor.hide(); | ||||
|         this._objInspector.hide(); | ||||
|  | ||||
|         this._open = false; | ||||
|         this.actor.remove_all_transitions(); | ||||
|         this.remove_all_transitions(); | ||||
|  | ||||
|         this.setBorderPaintTarget(null); | ||||
|  | ||||
| @@ -1126,16 +1167,15 @@ var LookingGlass = class LookingGlass { | ||||
|         let settings = St.Settings.get(); | ||||
|         let duration = Math.min(LG_ANIMATION_TIME / settings.slow_down_factor, | ||||
|                                 LG_ANIMATION_TIME); | ||||
|         this.actor.ease({ | ||||
|         this.ease({ | ||||
|             y: this._hiddenY, | ||||
|             duration, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this.actor.hide() | ||||
|             onComplete: () => this.hide(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     get isOpen() { | ||||
|         return this._open; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(LookingGlass.prototype); | ||||
| }); | ||||
|   | ||||
| @@ -55,7 +55,7 @@ var MouseSpriteContent = GObject.registerClass({ | ||||
|         return [true, this._texture.get_width(), this._texture.get_height()]; | ||||
|     } | ||||
|  | ||||
|     vfunc_paint_content(actor, node) { | ||||
|     vfunc_paint_content(actor, node, _paintContext) { | ||||
|         if (!this._texture) | ||||
|             return; | ||||
|  | ||||
| @@ -118,7 +118,7 @@ var Magnifier = class Magnifier { | ||||
|         }); | ||||
|  | ||||
|         // Export to dbus. | ||||
|         (new MagnifierDBus.ShellMagnifier()); | ||||
|         new MagnifierDBus.ShellMagnifier(); | ||||
|         this.setActive(St.Settings.get().magnifier_active); | ||||
|     } | ||||
|  | ||||
| @@ -141,7 +141,7 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * setActive: | ||||
|      * Show/hide all the zoom regions. | ||||
|      * @activate:   Boolean to activate or de-activate the magnifier. | ||||
|      * @param {bool} activate: Boolean to activate or de-activate the magnifier. | ||||
|      */ | ||||
|     setActive(activate) { | ||||
|         let isActive = this.isActive(); | ||||
| @@ -177,7 +177,7 @@ var Magnifier = class Magnifier { | ||||
|  | ||||
|     /** | ||||
|      * isActive: | ||||
|      * @return  Whether the magnifier is active (boolean). | ||||
|      * @returns {bool} Whether the magnifier is active. | ||||
|      */ | ||||
|     isActive() { | ||||
|         // Sufficient to check one ZoomRegion since Magnifier's active | ||||
| @@ -212,7 +212,7 @@ var Magnifier = class Magnifier { | ||||
|  | ||||
|     /** | ||||
|      * isTrackingMouse: | ||||
|      * Is the magnifier tracking the mouse currently? | ||||
|      * @returns {bool} whether the magnifier is currently tracking the mouse | ||||
|      */ | ||||
|     isTrackingMouse() { | ||||
|         return !!this._mouseTrackingId; | ||||
| @@ -222,7 +222,7 @@ var Magnifier = class Magnifier { | ||||
|      * scrollToMousePos: | ||||
|      * Position all zoom regions' ROI relative to the current location of the | ||||
|      * system pointer. | ||||
|      * @return      true. | ||||
|      * @returns {bool} true. | ||||
|      */ | ||||
|     scrollToMousePos() { | ||||
|         let [xMouse, yMouse] = global.get_pointer(); | ||||
| @@ -247,17 +247,17 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * createZoomRegion: | ||||
|      * Create a ZoomRegion instance with the given properties. | ||||
|      * @xMagFactor:     The power to set horizontal magnification of the | ||||
|      *                  ZoomRegion.  A value of 1.0 means no magnification.  A | ||||
|      *                  value of 2.0 doubles the size. | ||||
|      * @yMagFactor:     The power to set the vertical magnification of the | ||||
|      *                  ZoomRegion. | ||||
|      * @roi             Object in the form { x, y, width, height } that | ||||
|      *                  defines the region to magnify.  Given in unmagnified | ||||
|      *                  coordinates. | ||||
|      * @viewPort        Object in the form { x, y, width, height } that defines | ||||
|      *                  the position of the ZoomRegion on screen. | ||||
|      * @return          The newly created ZoomRegion. | ||||
|      * @param {number} xMagFactor: | ||||
|      *     The power to set horizontal magnification of the ZoomRegion. A value | ||||
|      *     of 1.0 means no magnification, a value of 2.0 doubles the size. | ||||
|      * @param {number} yMagFactor: | ||||
|      *    The power to set the vertical magnification of the ZoomRegion. | ||||
|      * @param {{x: number, y: number, width: number, height: number}} roi: | ||||
|      *    The reg Object that defines the region to magnify, given in | ||||
|      *    unmagnified coordinates. | ||||
|      * @param {{x: number, y: number, width: number, height: number}} viewPort: | ||||
|      *     Object that defines the position of the ZoomRegion on screen. | ||||
|      * @returns {ZoomRegion} the newly created ZoomRegion. | ||||
|      */ | ||||
|     createZoomRegion(xMagFactor, yMagFactor, roi, viewPort) { | ||||
|         let zoomRegion = new ZoomRegion(this, this._cursorRoot); | ||||
| @@ -277,7 +277,7 @@ var Magnifier = class Magnifier { | ||||
|      * addZoomRegion: | ||||
|      * Append the given ZoomRegion to the list of currently defined ZoomRegions | ||||
|      * for this Magnifier instance. | ||||
|      * @zoomRegion:     The zoomRegion to add. | ||||
|      * @param {ZoomRegion} zoomRegion: The zoomRegion to add. | ||||
|      */ | ||||
|     addZoomRegion(zoomRegion) { | ||||
|         if (zoomRegion) { | ||||
| @@ -290,7 +290,7 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * getZoomRegions: | ||||
|      * Return a list of ZoomRegion's for this Magnifier. | ||||
|      * @return:     The Magnifier's zoom region list (array). | ||||
|      * @returns {number[]} The Magnifier's zoom region list. | ||||
|      */ | ||||
|     getZoomRegions() { | ||||
|         return this._zoomRegions; | ||||
| @@ -330,7 +330,7 @@ var Magnifier = class Magnifier { | ||||
|         this.setCrosshairsClip(clip); | ||||
|  | ||||
|         let theCrossHairs = this._crossHairs; | ||||
|         this._zoomRegions.forEach (zoomRegion => { | ||||
|         this._zoomRegions.forEach(zoomRegion => { | ||||
|             zoomRegion.addCrosshairs(theCrossHairs); | ||||
|         }); | ||||
|     } | ||||
| @@ -338,7 +338,7 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * setCrosshairsVisible: | ||||
|      * Show or hide the cross hair. | ||||
|      * @visible    Flag that indicates show (true) or hide (false). | ||||
|      * @param {bool} visible: Flag that indicates show (true) or hide (false). | ||||
|      */ | ||||
|     setCrosshairsVisible(visible) { | ||||
|         if (visible) { | ||||
| @@ -346,6 +346,7 @@ var Magnifier = class Magnifier { | ||||
|                 this.addCrosshairs(); | ||||
|             this._crossHairs.show(); | ||||
|         } else { | ||||
|             // eslint-disable-next-line no-lonely-if | ||||
|             if (this._crossHairs) | ||||
|                 this._crossHairs.hide(); | ||||
|         } | ||||
| @@ -354,7 +355,7 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * setCrosshairsColor: | ||||
|      * Set the color of the crosshairs for all ZoomRegions. | ||||
|      * @color:  The color as a string, e.g. '#ff0000ff' or 'red'. | ||||
|      * @param {string} color: The color as a string, e.g. '#ff0000ff' or 'red'. | ||||
|      */ | ||||
|     setCrosshairsColor(color) { | ||||
|         if (this._crossHairs) { | ||||
| @@ -366,7 +367,7 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * getCrosshairsColor: | ||||
|      * Get the color of the crosshairs. | ||||
|      * @return: The color as a string, e.g. '#0000ffff' or 'blue'. | ||||
|      * @returns {string} The color as a string, e.g. '#0000ffff' or 'blue'. | ||||
|      */ | ||||
|     getCrosshairsColor() { | ||||
|         if (this._crossHairs) { | ||||
| @@ -380,8 +381,8 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * setCrosshairsThickness: | ||||
|      * Set the crosshairs thickness for all ZoomRegions. | ||||
|      * @thickness:  The width of the vertical and horizontal lines of the | ||||
|      *              crosshairs. | ||||
|      * @param {number} thickness: The width of the vertical and | ||||
|      *     horizontal lines of the crosshairs. | ||||
|      */ | ||||
|     setCrosshairsThickness(thickness) { | ||||
|         if (this._crossHairs) | ||||
| @@ -391,8 +392,8 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * getCrosshairsThickness: | ||||
|      * Get the crosshairs thickness. | ||||
|      * @return: The width of the vertical and horizontal lines of the | ||||
|      *          crosshairs. | ||||
|      * @returns {number} The width of the vertical and horizontal | ||||
|      *     lines of the crosshairs. | ||||
|      */ | ||||
|     getCrosshairsThickness() { | ||||
|         if (this._crossHairs) | ||||
| @@ -403,7 +404,8 @@ var Magnifier = class Magnifier { | ||||
|  | ||||
|     /** | ||||
|      * setCrosshairsOpacity: | ||||
|      * @opacity:    Value between 0.0 (transparent) and 1.0 (fully opaque). | ||||
|      * @param {number} opacity: Value between 0.0 (transparent) | ||||
|      *     and 1.0 (fully opaque). | ||||
|      */ | ||||
|     setCrosshairsOpacity(opacity) { | ||||
|         if (this._crossHairs) | ||||
| @@ -412,7 +414,7 @@ var Magnifier = class Magnifier { | ||||
|  | ||||
|     /** | ||||
|      * getCrosshairsOpacity: | ||||
|      * @return:     Value between 0.0 (transparent) and 1.0 (fully opaque). | ||||
|      * @returns {number} Value between 0.0 (transparent) and 1.0 (fully opaque). | ||||
|      */ | ||||
|     getCrosshairsOpacity() { | ||||
|         if (this._crossHairs) | ||||
| @@ -424,8 +426,8 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * setCrosshairsLength: | ||||
|      * Set the crosshairs length for all ZoomRegions. | ||||
|      * @length: The length of the vertical and horizontal lines making up the | ||||
|      *          crosshairs. | ||||
|      * @param {number} length: The length of the vertical and horizontal | ||||
|      *     lines making up the crosshairs. | ||||
|      */ | ||||
|     setCrosshairsLength(length) { | ||||
|         if (this._crossHairs) | ||||
| @@ -435,8 +437,8 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * getCrosshairsLength: | ||||
|      * Get the crosshairs length. | ||||
|      * @return: The length of the vertical and horizontal lines making up the | ||||
|      *          crosshairs. | ||||
|      * @returns {number} The length of the vertical and horizontal | ||||
|      *     lines making up the crosshairs. | ||||
|      */ | ||||
|     getCrosshairsLength() { | ||||
|         if (this._crossHairs) | ||||
| @@ -448,7 +450,7 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * setCrosshairsClip: | ||||
|      * Set whether the crosshairs are clipped at their intersection. | ||||
|      * @clip:   Flag to indicate whether to clip the crosshairs. | ||||
|      * @param {bool} clip: Flag to indicate whether to clip the crosshairs. | ||||
|      */ | ||||
|     setCrosshairsClip(clip) { | ||||
|         if (!this._crossHairs) | ||||
| @@ -461,18 +463,18 @@ var Magnifier = class Magnifier { | ||||
|     /** | ||||
|      * getCrosshairsClip: | ||||
|      * Get whether the crosshairs are clipped by the mouse image. | ||||
|      * @return:   Whether the crosshairs are clipped. | ||||
|      * @returns {bool} Whether the crosshairs are clipped. | ||||
|      */ | ||||
|     getCrosshairsClip() { | ||||
|         if (this._crossHairs) { | ||||
|             let [clipWidth, clipHeight] = this._crossHairs.getClip(); | ||||
|             return (clipWidth > 0 && clipHeight > 0); | ||||
|             return clipWidth > 0 && clipHeight > 0; | ||||
|         } else { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     //// Private methods //// | ||||
|     // Private methods // | ||||
|  | ||||
|     _updateMouseSprite() { | ||||
|         this._updateSpriteTexture(); | ||||
| @@ -623,9 +625,8 @@ var Magnifier = class Magnifier { | ||||
|  | ||||
|     _updateLensMode() { | ||||
|         // Applies only to the first zoom region. | ||||
|         if (this._zoomRegions.length) { | ||||
|         if (this._zoomRegions.length) | ||||
|             this._zoomRegions[0].setLensMode(this._settings.get_boolean(LENS_MODE_KEY)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _updateClampMode() { | ||||
| @@ -772,15 +773,16 @@ var ZoomRegion = class ZoomRegion { | ||||
|     } | ||||
|  | ||||
|     _updateScreenPosition() { | ||||
|         if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE) | ||||
|         if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE) { | ||||
|             this._setViewPort({ | ||||
|                 x: this._viewPortX, | ||||
|                 y: this._viewPortY, | ||||
|                 width: this._viewPortWidth, | ||||
|                 height: this._viewPortHeight | ||||
|                 height: this._viewPortHeight, | ||||
|             }); | ||||
|         else | ||||
|         } else { | ||||
|             this.setScreenPosition(this._screenPosition); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _updateFocus(caller, event) { | ||||
| @@ -818,7 +820,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * setActive: | ||||
|      * @activate:   Boolean to show/hide the ZoomRegion. | ||||
|      * @param {bool} activate: Boolean to show/hide the ZoomRegion. | ||||
|      */ | ||||
|     setActive(activate) { | ||||
|         if (activate == this.isActive()) | ||||
| @@ -844,7 +846,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * isActive: | ||||
|      * @return  Whether this ZoomRegion is active (boolean). | ||||
|      * @returns {bool} Whether this ZoomRegion is active | ||||
|      */ | ||||
|     isActive() { | ||||
|         return this._magView != null; | ||||
| @@ -852,24 +854,24 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * setMagFactor: | ||||
|      * @xMagFactor:     The power to set the horizontal magnification factor to | ||||
|      *                  of the magnified view.  A value of 1.0 means no | ||||
|      *                  magnification.  A value of 2.0 doubles the size. | ||||
|      * @yMagFactor:     The power to set the vertical magnification factor to | ||||
|      *                  of the magnified view. | ||||
|      * @param {number} xMagFactor: The power to set the horizontal | ||||
|      *     magnification factor to of the magnified view. A value of 1.0 | ||||
|      *     means no magnification. A value of 2.0 doubles the size. | ||||
|      * @param {number} yMagFactor: The power to set the vertical | ||||
|      *     magnification factor to of the magnified view. | ||||
|      */ | ||||
|     setMagFactor(xMagFactor, yMagFactor) { | ||||
|         this._changeROI({ xMagFactor: xMagFactor, | ||||
|                           yMagFactor: yMagFactor, | ||||
|         this._changeROI({ xMagFactor, | ||||
|                           yMagFactor, | ||||
|                           redoCursorTracking: this._followingCursor }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * getMagFactor: | ||||
|      * @return  an array, [xMagFactor, yMagFactor], containing the horizontal | ||||
|      *          and vertical magnification powers.  A value of 1.0 means no | ||||
|      *          magnification.  A value of 2.0 means the contents are doubled | ||||
|      *          in size, and so on. | ||||
|      * @returns {number[]} an array, [xMagFactor, yMagFactor], containing | ||||
|      *     the horizontal and vertical magnification powers. A value of | ||||
|      *     1.0 means no magnification. A value of 2.0 means the contents | ||||
|      *     are doubled in size, and so on. | ||||
|      */ | ||||
|     getMagFactor() { | ||||
|         return [this._xMagFactor, this._yMagFactor]; | ||||
| @@ -877,7 +879,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * setMouseTrackingMode | ||||
|      * @mode:     One of the enum MouseTrackingMode values. | ||||
|      * @param {GDesktopEnums.MagnifierMouseTrackingMode} mode: the new mode | ||||
|      */ | ||||
|     setMouseTrackingMode(mode) { | ||||
|         if (mode >= GDesktopEnums.MagnifierMouseTrackingMode.NONE && | ||||
| @@ -887,7 +889,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * getMouseTrackingMode | ||||
|      * @return:     One of the enum MouseTrackingMode values. | ||||
|      * @returns {GDesktopEnums.MagnifierMouseTrackingMode} the current mode | ||||
|      */ | ||||
|     getMouseTrackingMode() { | ||||
|         return this._mouseTrackingMode; | ||||
| @@ -895,7 +897,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * setFocusTrackingMode | ||||
|      * @mode:     One of the enum FocusTrackingMode values. | ||||
|      * @param {GDesktopEnums.MagnifierFocusTrackingMode} mode: the new mode | ||||
|      */ | ||||
|     setFocusTrackingMode(mode) { | ||||
|         this._focusTrackingMode = mode; | ||||
| @@ -904,7 +906,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * setCaretTrackingMode | ||||
|      * @mode:     One of the enum CaretTrackingMode values. | ||||
|      * @param {GDesktopEnums.MagnifierCaretTrackingMode} mode: the new mode | ||||
|      */ | ||||
|     setCaretTrackingMode(mode) { | ||||
|         this._caretTrackingMode = mode; | ||||
| @@ -934,9 +936,9 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * setViewPort | ||||
|      * Sets the position and size of the ZoomRegion on screen. | ||||
|      * @viewPort:   Object defining the position and size of the view port. | ||||
|      *              It has members x, y, width, height.  The values are in | ||||
|      *              stage coordinate space. | ||||
|      * @param {{x: number, y: number, width: number, height: number}} viewPort: | ||||
|      *     Object defining the position and size of the view port. | ||||
|      *     The values are in stage coordinate space. | ||||
|      */ | ||||
|     setViewPort(viewPort) { | ||||
|         this._setViewPort(viewPort); | ||||
| @@ -946,9 +948,9 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * setROI | ||||
|      * Sets the "region of interest" that the ZoomRegion is magnifying. | ||||
|      * @roi:    Object that defines the region of the screen to magnify.  It | ||||
|      *          has members x, y, width, height.  The values are in | ||||
|      *          screen (unmagnified) coordinate space. | ||||
|      * @param {{x: number, y: number, width: number, height: number}} roi: | ||||
|      *     Object that defines the region of the screen to magnify. | ||||
|      *     The values are in screen (unmagnified) coordinate space. | ||||
|      */ | ||||
|     setROI(roi) { | ||||
|         if (roi.width <= 0 || roi.height <= 0) | ||||
| @@ -966,8 +968,8 @@ var ZoomRegion = class ZoomRegion { | ||||
|      * Retrieves the "region of interest" -- the rectangular bounds of that part | ||||
|      * of the desktop that the magnified view is showing (x, y, width, height). | ||||
|      * The bounds are given in non-magnified coordinates. | ||||
|      * @return  an array, [x, y, width, height], representing the bounding | ||||
|      *          rectangle of what is shown in the magnified view. | ||||
|      * @returns {number[]} an array, [x, y, width, height], representing | ||||
|      *     the bounding rectangle of what is shown in the magnified view. | ||||
|      */ | ||||
|     getROI() { | ||||
|         let roiWidth = this._viewPortWidth / this._xMagFactor; | ||||
| @@ -982,18 +984,18 @@ var ZoomRegion = class ZoomRegion { | ||||
|      * setLensMode: | ||||
|      * Turn lens mode on/off.  In full screen mode, lens mode does nothing since | ||||
|      * a lens the size of the screen is pointless. | ||||
|      * @lensMode:   A boolean to set the sense of lens mode. | ||||
|      * @param {bool} lensMode: Whether lensMode should be active | ||||
|      */ | ||||
|     setLensMode(lensMode) { | ||||
|         this._lensMode = lensMode; | ||||
|         if (!this._lensMode) | ||||
|             this.setScreenPosition (this._screenPosition); | ||||
|             this.setScreenPosition(this._screenPosition); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * isLensMode: | ||||
|      * Is lens mode on or off? | ||||
|      * @return  The lens mode state as a boolean. | ||||
|      * @returns {bool} The lens mode state. | ||||
|      */ | ||||
|     isLensMode() { | ||||
|         return this._lensMode; | ||||
| @@ -1003,7 +1005,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|      * setClampScrollingAtEdges: | ||||
|      * Stop vs. allow scrolling of the magnified contents when it scroll beyond | ||||
|      * the edges of the screen. | ||||
|      * @clamp:   Boolean to turn on/off clamping. | ||||
|      * @param {bool} clamp: Boolean to turn on/off clamping. | ||||
|      */ | ||||
|     setClampScrollingAtEdges(clamp) { | ||||
|         this._clampScrollingAtEdges = clamp; | ||||
| @@ -1087,9 +1089,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|      * setScreenPosition: | ||||
|      * Positions the zoom region to one of the enumerated positions on the | ||||
|      * screen. | ||||
|      * @position:   one of Magnifier.FULL_SCREEN, Magnifier.TOP_HALF, | ||||
|      *              Magnifier.BOTTOM_HALF,Magnifier.LEFT_HALF, or | ||||
|      *              Magnifier.RIGHT_HALF. | ||||
|      * @param {GDesktopEnums.MagnifierScreenPosition} inPosition: the position | ||||
|      */ | ||||
|     setScreenPosition(inPosition) { | ||||
|         switch (inPosition) { | ||||
| @@ -1115,7 +1115,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|      * getScreenPosition: | ||||
|      * Tell the outside world what the current mode is -- magnifiying the | ||||
|      * top half, bottom half, etc. | ||||
|      * @return:  the current mode. | ||||
|      * @returns {GDesktopEnums.MagnifierScreenPosition}: the current position. | ||||
|      */ | ||||
|     getScreenPosition() { | ||||
|         return this._screenPosition; | ||||
| @@ -1124,7 +1124,8 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * scrollToMousePos: | ||||
|      * Set the region of interest based on the position of the system pointer. | ||||
|      * @return:     Whether the system mouse pointer is over the magnified view. | ||||
|      * @returns {bool}: Whether the system mouse pointer is over the | ||||
|      *     magnified view. | ||||
|      */ | ||||
|     scrollToMousePos() { | ||||
|         this._followingCursor = true; | ||||
| @@ -1161,8 +1162,8 @@ var ZoomRegion = class ZoomRegion { | ||||
|      * scrollContentsTo: | ||||
|      * Shift the contents of the magnified view such it is centered on the given | ||||
|      * coordinate. | ||||
|      * @x:      The x-coord of the point to center on. | ||||
|      * @y:      The y-coord of the point to center on. | ||||
|      * @param {number} x: The x-coord of the point to center on. | ||||
|      * @param {number} y: The y-coord of the point to center on. | ||||
|      */ | ||||
|     scrollContentsTo(x, y) { | ||||
|         this._clearScrollContentsTimer(); | ||||
| @@ -1175,22 +1176,21 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * addCrosshairs: | ||||
|      * Add crosshairs centered on the magnified mouse. | ||||
|      * @crossHairs: Crosshairs instance | ||||
|      * @param {Crosshairs} crossHairs: Crosshairs instance | ||||
|      */ | ||||
|     addCrosshairs(crossHairs) { | ||||
|         this._crossHairs = crossHairs; | ||||
|  | ||||
|         // If the crossHairs is not already within a larger container, add it | ||||
|         // to this zoom region.  Otherwise, add a clone. | ||||
|         if (crossHairs && this.isActive()) { | ||||
|         if (crossHairs && this.isActive()) | ||||
|             this._crossHairsActor = crossHairs.addToZoomRegion(this, this._mouseActor); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * setInvertLightness: | ||||
|      * Set whether to invert the lightness of the magnified view. | ||||
|      * @flag    Boolean to either invert brightness (true), or not (false). | ||||
|      * @param {bool} flag: whether brightness should be inverted | ||||
|      */ | ||||
|     setInvertLightness(flag) { | ||||
|         this._invertLightness = flag; | ||||
| @@ -1201,7 +1201,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * getInvertLightness: | ||||
|      * Retrieve whether the lightness is inverted. | ||||
|      * @return    Boolean indicating inversion (true), or not (false). | ||||
|      * @returns {bool} whether brightness should be inverted | ||||
|      */ | ||||
|     getInvertLightness() { | ||||
|         return this._invertLightness; | ||||
| @@ -1210,9 +1210,9 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * setColorSaturation: | ||||
|      * Set the color saturation of the magnified view. | ||||
|      * @sauration  A value from 0.0 to 1.0 that defines the color | ||||
|      *             saturation, with 0.0 defining no color (grayscale), | ||||
|      *             and 1.0 defining full color. | ||||
|      * @param {number} saturation: A value from 0.0 to 1.0 that defines | ||||
|      *     the color saturation, with 0.0 defining no color (grayscale), | ||||
|      *     and 1.0 defining full color. | ||||
|      */ | ||||
|     setColorSaturation(saturation) { | ||||
|         this._colorSaturation = saturation; | ||||
| @@ -1223,6 +1223,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * getColorSaturation: | ||||
|      * Retrieve the color saturation of the magnified view. | ||||
|      * @returns {number} the color saturation | ||||
|      */ | ||||
|     getColorSaturation() { | ||||
|         return this._colorSaturation; | ||||
| @@ -1231,10 +1232,14 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * setBrightness: | ||||
|      * Alter the brightness of the magnified view. | ||||
|      * @brightness  Object containing the contrast for the red, green, | ||||
|      *              and blue channels.  Values of 0.0 represent "standard" | ||||
|      *              brightness (no change), whereas values less or greater than | ||||
|      *              0.0 indicate decreased or incresaed brightness, respectively. | ||||
|      * @param {Object} brightness: Object containing the contrast for the | ||||
|      *     red, green, and blue channels. Values of 0.0 represent "standard" | ||||
|      *     brightness (no change), whereas values less or greater than | ||||
|      *     0.0 indicate decreased or incresaed brightness, respectively. | ||||
|      * | ||||
|      *     {number} brightness.r - the red component | ||||
|      *     {number} brightness.g - the green component | ||||
|      *     {number} brightness.b - the blue component | ||||
|      */ | ||||
|     setBrightness(brightness) { | ||||
|         this._brightness.r = brightness.r; | ||||
| @@ -1247,10 +1252,14 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * setContrast: | ||||
|      * Alter the contrast of the magnified view. | ||||
|      * @contrast    Object containing the contrast for the red, green, | ||||
|      *              and blue channels.  Values of 0.0 represent "standard" | ||||
|      *              contrast (no change), whereas values less or greater than | ||||
|      *              0.0 indicate decreased or incresaed contrast, respectively. | ||||
|      * @param {Object} contrast: Object containing the contrast for the | ||||
|      *     red, green, and blue channels. Values of 0.0 represent "standard" | ||||
|      *     contrast (no change), whereas values less or greater than | ||||
|      *     0.0 indicate decreased or incresaed contrast, respectively. | ||||
|      * | ||||
|      *     {number} contrast.r - the red component | ||||
|      *     {number} contrast.g - the green component | ||||
|      *     {number} contrast.b - the blue component | ||||
|      */ | ||||
|     setContrast(contrast) { | ||||
|         this._contrast.r = contrast.r; | ||||
| @@ -1263,8 +1272,8 @@ var ZoomRegion = class ZoomRegion { | ||||
|     /** | ||||
|      * getContrast: | ||||
|      * Retrieve the contrast of the magnified view. | ||||
|      * @return  Object containing the contrast for the red, green, | ||||
|      *          and blue channels. | ||||
|      * @returns {{r: number, g: number, b: number}}: Object containing | ||||
|      *     the contrast for the red, green, and blue channels. | ||||
|      */ | ||||
|     getContrast() { | ||||
|         let contrast = {}; | ||||
| @@ -1274,15 +1283,15 @@ var ZoomRegion = class ZoomRegion { | ||||
|         return contrast; | ||||
|     } | ||||
|  | ||||
|     //// Private methods //// | ||||
|     // Private methods // | ||||
|  | ||||
|     _createActors() { | ||||
|         // The root actor for the zoom region | ||||
|         this._magView = new St.Bin({ style_class: 'magnifier-zoom-region', x_fill: true, y_fill: true }); | ||||
|         this._magView = new St.Bin({ style_class: 'magnifier-zoom-region' }); | ||||
|         global.stage.add_actor(this._magView); | ||||
|  | ||||
|         // hide the magnified region from CLUTTER_PICK_ALL | ||||
|         Shell.util_set_hidden_from_pick (this._magView, true); | ||||
|         Shell.util_set_hidden_from_pick(this._magView, true); | ||||
|  | ||||
|         // Add a group to clip the contents of the magnified view. | ||||
|         let mainGroup = new Clutter.Actor({ clip_to_allocation: true }); | ||||
| @@ -1290,7 +1299,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|         // Add a background for when the magnified uiGroup is scrolled | ||||
|         // out of view (don't want to see desktop showing through). | ||||
|         this._background = (new Background.SystemBackground()).actor; | ||||
|         this._background = new Background.SystemBackground(); | ||||
|         mainGroup.add_actor(this._background); | ||||
|  | ||||
|         // Clone the group that contains all of UI on the screen.  This is the | ||||
| @@ -1322,7 +1331,7 @@ var ZoomRegion = class ZoomRegion { | ||||
|  | ||||
|     _destroyActors() { | ||||
|         if (this._mouseActor == this._mouseSourceActor) | ||||
|             this._mouseActor.get_parent().remove_actor (this._mouseActor); | ||||
|             this._mouseActor.get_parent().remove_actor(this._mouseActor); | ||||
|         if (this._crossHairs) | ||||
|             this._crossHairs.removeFromParent(this._crossHairsActor); | ||||
|  | ||||
| @@ -1402,11 +1411,12 @@ var ZoomRegion = class ZoomRegion { | ||||
|         // If in lens mode, move the magnified view such that it is centered | ||||
|         // over the actual mouse. However, in full screen mode, the "lens" is | ||||
|         // the size of the screen -- pointless to move such a large lens around. | ||||
|         if (this._lensMode && !this._isFullScreen()) | ||||
|         if (this._lensMode && !this._isFullScreen()) { | ||||
|             this._setViewPort({ x: this._xCenter - this._viewPortWidth / 2, | ||||
|                                 y: this._yCenter - this._viewPortHeight / 2, | ||||
|                                 width: this._viewPortWidth, | ||||
|                                 height: this._viewPortHeight }, true); | ||||
|         } | ||||
|  | ||||
|         this._updateCloneGeometry(); | ||||
|         this._updateMousePosition(); | ||||
| @@ -1420,10 +1430,9 @@ var ZoomRegion = class ZoomRegion { | ||||
|             let xMouse = this._magnifier.xMouse; | ||||
|             let yMouse = this._magnifier.yMouse; | ||||
|  | ||||
|             mouseIsOver = ( | ||||
|             mouseIsOver = | ||||
|                 xMouse >= this._viewPortX && xMouse < (this._viewPortX + this._viewPortWidth) && | ||||
|                 yMouse >= this._viewPortY && yMouse < (this._viewPortY + this._viewPortHeight) | ||||
|             ); | ||||
|                 yMouse >= this._viewPortY && yMouse < (this._viewPortY + this._viewPortHeight); | ||||
|         } | ||||
|         return mouseIsOver; | ||||
|     } | ||||
| @@ -1448,13 +1457,12 @@ var ZoomRegion = class ZoomRegion { | ||||
|         let xMouse = this._magnifier.xMouse; | ||||
|         let yMouse = this._magnifier.yMouse; | ||||
|  | ||||
|         if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) { | ||||
|         if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) | ||||
|             return this._centerFromPointProportional(xMouse, yMouse); | ||||
|         } else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) { | ||||
|         else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) | ||||
|             return this._centerFromPointPush(xMouse, yMouse); | ||||
|         } else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) { | ||||
|         else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) | ||||
|             return this._centerFromPointCentered(xMouse, yMouse); | ||||
|         } | ||||
|  | ||||
|         return null; // Should never be hit | ||||
|     } | ||||
| @@ -1496,14 +1504,14 @@ var ZoomRegion = class ZoomRegion { | ||||
|         let yRoiBottom = yRoi + heightRoi - cursorHeight; | ||||
|  | ||||
|         if (xPoint < xRoi) | ||||
|             xPos -= (xRoi - xPoint); | ||||
|             xPos -= xRoi - xPoint; | ||||
|         else if (xPoint > xRoiRight) | ||||
|             xPos += (xPoint - xRoiRight); | ||||
|             xPos += xPoint - xRoiRight; | ||||
|  | ||||
|         if (yPoint < yRoi) | ||||
|             yPos -= (yRoi - yPoint); | ||||
|             yPos -= yRoi - yPoint; | ||||
|         else if (yPoint > yRoiBottom) | ||||
|             yPos += (yPoint - yRoiBottom); | ||||
|             yPos += yPoint - yRoiBottom; | ||||
|  | ||||
|         return [xPos, yPos]; | ||||
|     } | ||||
| @@ -1587,8 +1595,9 @@ var ZoomRegion = class ZoomRegion { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var Crosshairs = class Crosshairs { | ||||
|     constructor() { | ||||
| var Crosshairs = GObject.registerClass( | ||||
| class Crosshairs extends Clutter.Actor { | ||||
|     _init() { | ||||
|  | ||||
|         // Set the group containing the crosshairs to three times the desktop | ||||
|         // size in case the crosshairs need to appear to be infinite in | ||||
| @@ -1596,19 +1605,19 @@ var Crosshairs = class Crosshairs { | ||||
|         let groupWidth = global.screen_width * 3; | ||||
|         let groupHeight = global.screen_height * 3; | ||||
|  | ||||
|         this._actor = new Clutter.Actor({ | ||||
|         super._init({ | ||||
|             clip_to_allocation: false, | ||||
|             width: groupWidth, | ||||
|             height: groupHeight | ||||
|             height: groupHeight, | ||||
|         }); | ||||
|         this._horizLeftHair = new Clutter.Actor(); | ||||
|         this._horizRightHair = new Clutter.Actor(); | ||||
|         this._vertTopHair = new Clutter.Actor(); | ||||
|         this._vertBottomHair = new Clutter.Actor(); | ||||
|         this._actor.add_actor(this._horizLeftHair); | ||||
|         this._actor.add_actor(this._horizRightHair); | ||||
|         this._actor.add_actor(this._vertTopHair); | ||||
|         this._actor.add_actor(this._vertBottomHair); | ||||
|         this.add_actor(this._horizLeftHair); | ||||
|         this.add_actor(this._horizRightHair); | ||||
|         this.add_actor(this._vertTopHair); | ||||
|         this.add_actor(this._vertBottomHair); | ||||
|         this._clipSize = [0, 0]; | ||||
|         this._clones = []; | ||||
|         this.reCenter(); | ||||
| @@ -1618,7 +1627,7 @@ var Crosshairs = class Crosshairs { | ||||
|     } | ||||
|  | ||||
|     _monitorsChanged() { | ||||
|         this._actor.set_size(global.screen_width * 3, global.screen_height * 3); | ||||
|         this.set_size(global.screen_width * 3, global.screen_height * 3); | ||||
|         this.reCenter(); | ||||
|     } | ||||
|  | ||||
| @@ -1628,26 +1637,30 @@ var Crosshairs = class Crosshairs { | ||||
|     * already part of some other ZoomRegion, create a clone of the crosshairs | ||||
|     * actor, and add the clone instead.  Returns either the original or the | ||||
|     * clone. | ||||
|     * @zoomRegion:      The container to add the crosshairs group to. | ||||
|     * @magnifiedMouse:  The mouse actor for the zoom region -- used to | ||||
|     *                   position the crosshairs and properly layer them below | ||||
|     *                   the mouse. | ||||
|     * @return           The crosshairs actor, or its clone. | ||||
|     * @param {ZoomRegion} zoomRegion: The container to add the crosshairs | ||||
|     *     group to. | ||||
|     * @param {Clutter.Actor} magnifiedMouse: The mouse actor for the | ||||
|     *     zoom region -- used to position the crosshairs and properly | ||||
|     *     layer them below the mouse. | ||||
|     * @returns {Clutter.Actor} The crosshairs actor, or its clone. | ||||
|     */ | ||||
|     addToZoomRegion(zoomRegion, magnifiedMouse) { | ||||
|         let crosshairsActor = null; | ||||
|         if (zoomRegion && magnifiedMouse) { | ||||
|             let container = magnifiedMouse.get_parent(); | ||||
|             if (container) { | ||||
|                 crosshairsActor = this._actor; | ||||
|                 if (this._actor.get_parent() != null) { | ||||
|                     crosshairsActor = new Clutter.Clone({ source: this._actor }); | ||||
|                 crosshairsActor = this; | ||||
|                 if (this.get_parent() != null) { | ||||
|                     crosshairsActor = new Clutter.Clone({ source: this }); | ||||
|                     this._clones.push(crosshairsActor); | ||||
|  | ||||
|                     // Clones don't share visibility. | ||||
|                     this.bind_property('visible', crosshairsActor, 'visible', | ||||
|                                        GObject.BindingFlags.SYNC_CREATE); | ||||
|                 } | ||||
|                 crosshairsActor.visible = this._actor.visible; | ||||
|  | ||||
|                 container.add_actor(crosshairsActor); | ||||
|                 container.raise_child(magnifiedMouse, crosshairsActor); | ||||
|                 container.set_child_above_sibling(magnifiedMouse, crosshairsActor); | ||||
|                 let [xMouse, yMouse] = magnifiedMouse.get_position(); | ||||
|                 let [crosshairsWidth, crosshairsHeight] = crosshairsActor.get_size(); | ||||
|                 crosshairsActor.set_position(xMouse - crosshairsWidth / 2, yMouse - crosshairsHeight / 2); | ||||
| @@ -1658,12 +1671,13 @@ var Crosshairs = class Crosshairs { | ||||
|  | ||||
|     /** | ||||
|      * removeFromParent: | ||||
|      * @childActor: the actor returned from addToZoomRegion | ||||
|      * @param {Clutter.Actor} childActor: the actor returned from | ||||
|      *     addToZoomRegion | ||||
|      * Remove the crosshairs actor from its parent container, or destroy the | ||||
|      * child actor if it was just a clone of the crosshairs actor. | ||||
|      */ | ||||
|     removeFromParent(childActor) { | ||||
|         if (childActor == this._actor) | ||||
|         if (childActor == this) | ||||
|             childActor.get_parent().remove_actor(childActor); | ||||
|         else | ||||
|             childActor.destroy(); | ||||
| @@ -1672,7 +1686,7 @@ var Crosshairs = class Crosshairs { | ||||
|     /** | ||||
|      * setColor: | ||||
|      * Set the color of the crosshairs. | ||||
|      * @clutterColor:   The color as a Clutter.Color. | ||||
|      * @param {Clutter.Color} clutterColor: The color | ||||
|      */ | ||||
|     setColor(clutterColor) { | ||||
|         this._horizLeftHair.background_color = clutterColor; | ||||
| @@ -1684,7 +1698,7 @@ var Crosshairs = class Crosshairs { | ||||
|     /** | ||||
|      * getColor: | ||||
|      * Get the color of the crosshairs. | ||||
|      * @color:  The color as a Clutter.Color. | ||||
|      * @returns {ClutterColor} the crosshairs color | ||||
|      */ | ||||
|     getColor() { | ||||
|         return this._horizLeftHair.get_color(); | ||||
| @@ -1693,7 +1707,7 @@ var Crosshairs = class Crosshairs { | ||||
|     /** | ||||
|      * setThickness: | ||||
|      * Set the width of the vertical and horizontal lines of the crosshairs. | ||||
|      * @thickness | ||||
|      * @param {number} thickness: the new thickness value | ||||
|      */ | ||||
|     setThickness(thickness) { | ||||
|         this._horizLeftHair.set_height(thickness); | ||||
| @@ -1706,7 +1720,7 @@ var Crosshairs = class Crosshairs { | ||||
|     /** | ||||
|      * getThickness: | ||||
|      * Get the width of the vertical and horizontal lines of the crosshairs. | ||||
|      * @return:     The thickness of the crosshairs. | ||||
|      * @returns {number} The thickness of the crosshairs. | ||||
|      */ | ||||
|     getThickness() { | ||||
|         return this._horizLeftHair.get_height(); | ||||
| @@ -1715,7 +1729,8 @@ var Crosshairs = class Crosshairs { | ||||
|     /** | ||||
|      * setOpacity: | ||||
|      * Set how opaque the crosshairs are. | ||||
|      * @opacity:    Value between 0 (fully transparent) and 255 (full opaque). | ||||
|      * @param {number} opacity: Value between 0 (fully transparent) | ||||
|      *     and 255 (full opaque). | ||||
|      */ | ||||
|     setOpacity(opacity) { | ||||
|         // set_opacity() throws an exception for values outside the range | ||||
| @@ -1734,7 +1749,7 @@ var Crosshairs = class Crosshairs { | ||||
|     /** | ||||
|      * setLength: | ||||
|      * Set the length of the vertical and horizontal lines in the crosshairs. | ||||
|      * @length: The length of the crosshairs. | ||||
|      * @param {number} length: The length of the crosshairs. | ||||
|      */ | ||||
|     setLength(length) { | ||||
|         this._horizLeftHair.set_width(length); | ||||
| @@ -1747,7 +1762,7 @@ var Crosshairs = class Crosshairs { | ||||
|     /** | ||||
|      * getLength: | ||||
|      * Get the length of the vertical and horizontal lines in the crosshairs. | ||||
|      * @return: The length of the crosshairs. | ||||
|      * @returns {number} The length of the crosshairs. | ||||
|      */ | ||||
|     getLength() { | ||||
|         return this._horizLeftHair.get_width(); | ||||
| @@ -1757,8 +1772,8 @@ var Crosshairs = class Crosshairs { | ||||
|      * setClip: | ||||
|      * Set the width and height of the rectangle that clips the crosshairs at | ||||
|      * their intersection | ||||
|      * @size:   Array of [width, height] defining the size of the clip | ||||
|      *          rectangle. | ||||
|      * @param {number[]} size: Array of [width, height] defining the size | ||||
|      *     of the clip rectangle. | ||||
|      */ | ||||
|     setClip(size) { | ||||
|         if (size) { | ||||
| @@ -1773,37 +1788,15 @@ var Crosshairs = class Crosshairs { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * show: | ||||
|      * Show the crosshairs. | ||||
|      */ | ||||
|     show() { | ||||
|         this._actor.show(); | ||||
|         // Clones don't share visibility. | ||||
|         for (let i = 0; i < this._clones.length; i++) | ||||
|             this._clones[i].show(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * hide: | ||||
|      * Hide the crosshairs. | ||||
|      */ | ||||
|     hide() { | ||||
|         this._actor.hide(); | ||||
|         // Clones don't share visibility. | ||||
|         for (let i = 0; i < this._clones.length; i++) | ||||
|             this._clones[i].hide(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * reCenter: | ||||
|      * Reposition the horizontal and vertical hairs such that they cross at | ||||
|      * the center of crosshairs group.  If called with the dimensions of | ||||
|      * the clip rectangle, these are used to update the size of the clip. | ||||
|      * @clipSize:  Optional.  If present, an array of the form [width, height]. | ||||
|      * @param {number[]=} clipSize: If present, the clip's [width, height]. | ||||
|      */ | ||||
|     reCenter(clipSize) { | ||||
|         let [groupWidth, groupHeight] = this._actor.get_size(); | ||||
|         let [groupWidth, groupHeight] = this.get_size(); | ||||
|         let leftLength = this._horizLeftHair.get_width(); | ||||
|         let topLength = this._vertTopHair.get_height(); | ||||
|         let thickness = this._horizLeftHair.get_height(); | ||||
| @@ -1825,7 +1818,7 @@ var Crosshairs = class Crosshairs { | ||||
|         this._vertTopHair.set_position((groupWidth - thickness) / 2, top); | ||||
|         this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var MagShaderEffects = class MagShaderEffects { | ||||
|     constructor(uiGroupClone) { | ||||
| @@ -1858,7 +1851,7 @@ var MagShaderEffects = class MagShaderEffects { | ||||
|     /** | ||||
|      * setInvertLightness: | ||||
|      * Enable/disable invert lightness effect. | ||||
|      * @invertFlag:     Enabled flag. | ||||
|      * @param {bool} invertFlag: Enabled flag. | ||||
|      */ | ||||
|     setInvertLightness(invertFlag) { | ||||
|         this._inverse.set_enabled(invertFlag); | ||||
| @@ -1871,11 +1864,14 @@ var MagShaderEffects = class MagShaderEffects { | ||||
|     /** | ||||
|      * setBrightness: | ||||
|      * Set the brightness of the magnified view. | ||||
|      * @brightness: Object containing the brightness for the red, green, | ||||
|      *              and blue channels.  Values of 0.0 represent "standard" | ||||
|      *              brightness (no change), whereas values less or greater than | ||||
|      *              0.0 indicate decreased or incresaed brightness, | ||||
|      *              respectively. | ||||
|      * @param {Object} brightness: Object containing the contrast for the | ||||
|      *     red, green, and blue channels. Values of 0.0 represent "standard" | ||||
|      *     brightness (no change), whereas values less or greater than | ||||
|      *     0.0 indicate decreased or incresaed brightness, respectively. | ||||
|      * | ||||
|      *     {number} brightness.r - the red component | ||||
|      *     {number} brightness.g - the green component | ||||
|      *     {number} brightness.b - the blue component | ||||
|      */ | ||||
|     setBrightness(brightness) { | ||||
|         let bRed = brightness.r; | ||||
| @@ -1887,17 +1883,21 @@ var MagShaderEffects = class MagShaderEffects { | ||||
|         // it modifies the brightness and/or contrast. | ||||
|         let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast(); | ||||
|         this._brightnessContrast.set_enabled( | ||||
|             (bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE || | ||||
|              cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE) | ||||
|             bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE || | ||||
|              cRed != NO_CHANGE || cGreen != NO_CHANGE || cBlue != NO_CHANGE | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the contrast of the magnified view. | ||||
|      * @contrast:   Object containing the contrast for the red, green, | ||||
|      *              and blue channels.  Values of 0.0 represent "standard" | ||||
|      *              contrast (no change), whereas values less or greater than | ||||
|      *              0.0 indicate decreased or incresaed contrast, respectively. | ||||
|      * @param {Object} contrast: Object containing the contrast for the | ||||
|      *     red, green, and blue channels. Values of 0.0 represent "standard" | ||||
|      *     contrast (no change), whereas values less or greater than | ||||
|      *     0.0 indicate decreased or incresaed contrast, respectively. | ||||
|      * | ||||
|      *     {number} contrast.r - the red component | ||||
|      *     {number} contrast.g - the green component | ||||
|      *     {number} contrast.b - the blue component | ||||
|      */ | ||||
|     setContrast(contrast) { | ||||
|         let cRed = contrast.r; | ||||
|   | ||||
| @@ -32,7 +32,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|  | ||||
|     /** | ||||
|      * setActive: | ||||
|      * @activate:   Boolean to activate or de-activate the magnifier. | ||||
|      * @param {bool} activate: activate or de-activate the magnifier. | ||||
|      */ | ||||
|     setActive(activate) { | ||||
|         Main.magnifier.setActive(activate); | ||||
| @@ -40,7 +40,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|  | ||||
|     /** | ||||
|      * isActive: | ||||
|      * @return  Whether the magnifier is active (boolean). | ||||
|      * @returns {bool} Whether the magnifier is active. | ||||
|      */ | ||||
|     isActive() { | ||||
|         return Main.magnifier.isActive(); | ||||
| @@ -65,22 +65,25 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * createZoomRegion: | ||||
|      * Create a new ZoomRegion and return its object path. | ||||
|      * @xMagFactor:     The power to set horizontal magnification of the | ||||
|      *                  ZoomRegion.  A value of 1.0 means no magnification.  A | ||||
|      *                  value of 2.0 doubles the size. | ||||
|      * @yMagFactor:     The power to set the vertical magnification of the | ||||
|      *                  ZoomRegion. | ||||
|      * @roi             Array of integers defining the region of the | ||||
|      *                  screen/desktop to magnify.  The array has the form | ||||
|      *                  [left, top, right, bottom]. | ||||
|      * @viewPort        Array of integers, [left, top, right, bottom] that defines | ||||
|      *                  the position of the ZoomRegion on screen. | ||||
|      * @param {number} xMagFactor: | ||||
|      *     The power to set horizontal magnification of the ZoomRegion. | ||||
|      *     A value of 1.0 means no magnification. A value of 2.0 doubles | ||||
|      *     the size. | ||||
|      * @param {number} yMagFactor: | ||||
|      *     The power to set the vertical magnification of the | ||||
|      *     ZoomRegion. | ||||
|      * @param {number[]} roi | ||||
|      *     Array of integers defining the region of the screen/desktop | ||||
|      *     to magnify.  The array has the form [left, top, right, bottom]. | ||||
|      * @param {number[]} viewPort | ||||
|      *     Array of integers, [left, top, right, bottom] that defines | ||||
|      *     the position of the ZoomRegion on screen. | ||||
|      * | ||||
|      * FIXME: The arguments here are redundant, since the width and height of | ||||
|      *   the ROI are determined by the viewport and magnification factors. | ||||
|      *   We ignore the passed in width and height. | ||||
|      * | ||||
|      * @return          The newly created ZoomRegion. | ||||
|      * @returns {ZoomRegion} The newly created ZoomRegion. | ||||
|      */ | ||||
|     createZoomRegion(xMagFactor, yMagFactor, roi, viewPort) { | ||||
|         let ROI = { x: roi[0], y: roi[1], width: roi[2] - roi[0], height: roi[3] - roi[1] }; | ||||
| @@ -100,7 +103,9 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * addZoomRegion: | ||||
|      * Append the given ZoomRegion to the magnifier's list of ZoomRegions. | ||||
|      * @zoomerObjectPath:   The object path for the zoom region proxy. | ||||
|      * @param {string} zoomerObjectPath: The object path for the zoom | ||||
|      *     region proxy. | ||||
|      * @returns {bool} whether the region was added successfully | ||||
|      */ | ||||
|     addZoomRegion(zoomerObjectPath) { | ||||
|         let proxyAndZoomRegion = this._zoomers[zoomerObjectPath]; | ||||
| @@ -115,8 +120,8 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * getZoomRegions: | ||||
|      * Return a list of ZoomRegion object paths for this Magnifier. | ||||
|      * @return:     The Magnifier's zoom region list as an array of DBus object | ||||
|      *              paths. | ||||
|      * @returns {string[]}: The Magnifier's zoom region list as an array | ||||
|      *     of DBus object paths. | ||||
|      */ | ||||
|     getZoomRegions() { | ||||
|         // There may be more ZoomRegions in the magnifier itself than have | ||||
| @@ -125,7 +130,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|         let zoomRegions = Main.magnifier.getZoomRegions(); | ||||
|         let objectPaths = []; | ||||
|         let thoseZoomers = this._zoomers; | ||||
|         zoomRegions.forEach (aZoomRegion => { | ||||
|         zoomRegions.forEach(aZoomRegion => { | ||||
|             let found = false; | ||||
|             for (let objectPath in thoseZoomers) { | ||||
|                 let proxyAndZoomRegion = thoseZoomers[objectPath]; | ||||
| @@ -137,7 +142,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|             } | ||||
|             if (!found) { | ||||
|                 // Got a ZoomRegion with no DBus proxy, make one. | ||||
|                 let newPath =  ZOOM_SERVICE_PATH + '/zoomer' + _zoomRegionInstanceCount; | ||||
|                 let newPath = `${ZOOM_SERVICE_PATH}/zoomer${_zoomRegionInstanceCount}`; | ||||
|                 _zoomRegionInstanceCount++; | ||||
|                 let zoomRegionProxy = new ShellMagnifierZoomRegion(newPath, aZoomRegion); | ||||
|                 let proxyAndZoomer = {}; | ||||
| @@ -169,7 +174,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * fullScreenCapable: | ||||
|      * Consult if the Magnifier can magnify in full-screen mode. | ||||
|      * @return  Always return true. | ||||
|      * @returns {bool} Always return true. | ||||
|      */ | ||||
|     fullScreenCapable() { | ||||
|         return true; | ||||
| @@ -178,7 +183,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * setCrosswireSize: | ||||
|      * Set the crosswire size of all ZoomRegions. | ||||
|      * @size:   The thickness of each line in the cross wire. | ||||
|      * @param {number} size: The thickness of each line in the cross wire. | ||||
|      */ | ||||
|     setCrosswireSize(size) { | ||||
|         Main.magnifier.setCrosshairsThickness(size); | ||||
| @@ -187,7 +192,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * getCrosswireSize: | ||||
|      * Get the crosswire size of all ZoomRegions. | ||||
|      * @return:   The thickness of each line in the cross wire. | ||||
|      * @returns {number}: The thickness of each line in the cross wire. | ||||
|      */ | ||||
|     getCrosswireSize() { | ||||
|         return Main.magnifier.getCrosshairsThickness(); | ||||
| @@ -196,16 +201,16 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * setCrosswireLength: | ||||
|      * Set the crosswire length of all zoom-regions.. | ||||
|      * @size:   The length of each line in the cross wire. | ||||
|      * @param {number} length: The length of each line in the cross wire. | ||||
|      */ | ||||
|     setCrosswireLength(length) { | ||||
|         Main.magnifier.setCrosshairsLength(length); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * setCrosswireSize: | ||||
|      * Set the crosswire size of all zoom-regions. | ||||
|      * @size:   The thickness of each line in the cross wire. | ||||
|      * getCrosswireSize: | ||||
|      * Get the crosswire length of all zoom-regions. | ||||
|      * @returns {number} size: The length of each line in the cross wire. | ||||
|      */ | ||||
|     getCrosswireLength() { | ||||
|         return Main.magnifier.getCrosshairsLength(); | ||||
| @@ -214,7 +219,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * setCrosswireClip: | ||||
|      * Set if the crosswire will be clipped by the cursor image.. | ||||
|      * @clip:   Flag to indicate whether to clip the crosswire. | ||||
|      * @param {bool} clip: Flag to indicate whether to clip the crosswire. | ||||
|      */ | ||||
|     setCrosswireClip(clip) { | ||||
|         Main.magnifier.setCrosshairsClip(clip); | ||||
| @@ -223,7 +228,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * getCrosswireClip: | ||||
|      * Get the crosswire clip value. | ||||
|      * @return:   Whether the crosswire is clipped by the cursor image. | ||||
|      * @returns {bool}: Whether the crosswire is clipped by the cursor image. | ||||
|      */ | ||||
|     getCrosswireClip() { | ||||
|         return Main.magnifier.getCrosshairsClip(); | ||||
| @@ -232,7 +237,7 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * setCrosswireColor: | ||||
|      * Set the crosswire color of all ZoomRegions. | ||||
|      * @color:   Unsigned int of the form rrggbbaa. | ||||
|      * @param {number} color: Unsigned int of the form rrggbbaa. | ||||
|      */ | ||||
|     setCrosswireColor(color) { | ||||
|         Main.magnifier.setCrosshairsColor('#%08x'.format(color)); | ||||
| @@ -241,7 +246,8 @@ var ShellMagnifier = class ShellMagnifier { | ||||
|     /** | ||||
|      * getCrosswireClip: | ||||
|      * Get the crosswire color of all ZoomRegions. | ||||
|      * @return:   The crosswire color as an unsigned int in the form rrggbbaa. | ||||
|      * @returns {number}: The crosswire color as an unsigned int in | ||||
|      *     the form rrggbbaa. | ||||
|      */ | ||||
|     getCrosswireColor() { | ||||
|         let colorString = Main.magnifier.getCrosshairsColor(); | ||||
| @@ -266,11 +272,11 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * setMagFactor: | ||||
|      * @xMagFactor:     The power to set the horizontal magnification factor to | ||||
|      *                  of the magnified view.  A value of 1.0 means no | ||||
|      *                  magnification.  A value of 2.0 doubles the size. | ||||
|      * @yMagFactor:     The power to set the vertical magnification factor to | ||||
|      *                  of the magnified view. | ||||
|      * @param {number} xMagFactor: The power to set the horizontal | ||||
|      *     magnification factor to of the magnified view. A value of | ||||
|      *     1.0 means no magnification. A value of 2.0 doubles the size. | ||||
|      * @param {number} yMagFactor: The power to set the vertical | ||||
|      *     magnification factor to of the magnified view. | ||||
|      */ | ||||
|     setMagFactor(xMagFactor, yMagFactor) { | ||||
|         this._zoomRegion.setMagFactor(xMagFactor, yMagFactor); | ||||
| @@ -278,7 +284,7 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion { | ||||
|  | ||||
|     /** | ||||
|      * getMagFactor: | ||||
|      * @return  an array, [xMagFactor, yMagFactor], containing the horizontal | ||||
|      * @returns {number[]}: [xMagFactor, yMagFactor], containing the horizontal | ||||
|      *          and vertical magnification powers.  A value of 1.0 means no | ||||
|      *          magnification.  A value of 2.0 means the contents are doubled | ||||
|      *          in size, and so on. | ||||
| @@ -290,9 +296,9 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion { | ||||
|     /** | ||||
|      * setRoi: | ||||
|      * Sets the "region of interest" that the ZoomRegion is magnifying. | ||||
|      * @roi     Array, [left, top, right, bottom], defining the region of the | ||||
|      *          screen to magnify. The values are in screen (unmagnified) | ||||
|      *          coordinate space. | ||||
|      * @param {number[]} roi: [left, top, right, bottom], defining the | ||||
|      *     region of the screen to magnify. | ||||
|      *     The values are in screen (unmagnified) coordinate space. | ||||
|      */ | ||||
|     setRoi(roi) { | ||||
|         let roiObject = { x: roi[0], y: roi[1], width: roi[2] - roi[0], height: roi[3] - roi[1] }; | ||||
| @@ -304,7 +310,7 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion { | ||||
|      * Retrieves the "region of interest" -- the rectangular bounds of that part | ||||
|      * of the desktop that the magnified view is showing (x, y, width, height). | ||||
|      * The bounds are given in non-magnified coordinates. | ||||
|      * @return  an array, [left, top, right, bottom], representing the bounding | ||||
|      * @returns {Array}: [left, top, right, bottom], representing the bounding | ||||
|      *          rectangle of what is shown in the magnified view. | ||||
|      */ | ||||
|     getRoi() { | ||||
| @@ -317,10 +323,11 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion { | ||||
|     /** | ||||
|      * Set the "region of interest" by centering the given screen coordinate | ||||
|      * within the zoom region. | ||||
|      * @x       The x-coord of the point to place at the center of the zoom region. | ||||
|      * @y       The y-coord. | ||||
|      * @return  Whether the shift was successful (for GS-mag, this is always | ||||
|      *          true). | ||||
|      * @param {number} x: The x-coord of the point to place at the | ||||
|      *     center of the zoom region. | ||||
|      * @param {number} y: The y-coord. | ||||
|      * @returns {bool} Whether the shift was successful (for GS-mag, this | ||||
|      *     is always true). | ||||
|      */ | ||||
|     shiftContentsTo(x, y) { | ||||
|         this._zoomRegion.scrollContentsTo(x, y); | ||||
| @@ -330,8 +337,8 @@ var ShellMagnifierZoomRegion = class ShellMagnifierZoomRegion { | ||||
|     /** | ||||
|      * moveResize | ||||
|      * Sets the position and size of the ZoomRegion on screen. | ||||
|      * @viewPort    Array, [left, top, right, bottom], defining the position and | ||||
|      *              size on screen to place the zoom region. | ||||
|      * @param {number[]} viewPort: [left, top, right, bottom], defining | ||||
|      *     the position and size on screen to place the zoom region. | ||||
|      */ | ||||
|     moveResize(viewPort) { | ||||
|         let viewRect = { x: viewPort[0], y: viewPort[1], width: viewPort[2] - viewPort[0], height: viewPort[3] - viewPort[1] }; | ||||
|   | ||||
							
								
								
									
										112
									
								
								js/ui/main.js
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								js/ui/main.js
									
									
									
									
									
								
							| @@ -189,7 +189,7 @@ function _initializeUI() { | ||||
|  | ||||
|     messageTray = new MessageTray.MessageTray(); | ||||
|     panel = new Panel.Panel(); | ||||
|     keyboard = new Keyboard.Keyboard(); | ||||
|     keyboard = new Keyboard.KeyboardManager(); | ||||
|     notificationDaemon = new NotificationDaemon.NotificationDaemon(); | ||||
|     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); | ||||
|     componentManager = new Components.ComponentManager(); | ||||
| @@ -199,12 +199,12 @@ function _initializeUI() { | ||||
|     layoutManager.init(); | ||||
|     overview.init(); | ||||
|  | ||||
|     (new PointerA11yTimeout.PointerA11yTimeout()); | ||||
|     new PointerA11yTimeout.PointerA11yTimeout(); | ||||
|  | ||||
|     _a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA }); | ||||
|  | ||||
|     global.display.connect('overlay-key', () => { | ||||
|         if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE)) | ||||
|         if (!_a11ySettings.get_boolean(STICKY_KEYS_ENABLE)) | ||||
|             overview.toggle(); | ||||
|     }); | ||||
|  | ||||
| @@ -248,17 +248,17 @@ function _initializeUI() { | ||||
|     } | ||||
|  | ||||
|     layoutManager.connect('startup-complete', () => { | ||||
|         if (actionMode == Shell.ActionMode.NONE) { | ||||
|         if (actionMode == Shell.ActionMode.NONE) | ||||
|             actionMode = Shell.ActionMode.NORMAL; | ||||
|         } | ||||
|         if (screenShield) { | ||||
|  | ||||
|         if (screenShield) | ||||
|             screenShield.lockIfWasLocked(); | ||||
|         } | ||||
|  | ||||
|         if (sessionMode.currentMode != 'gdm' && | ||||
|             sessionMode.currentMode != 'initial-setup') { | ||||
|             GLib.log_structured(LOG_DOMAIN, GLib.LogLevelFlags.LEVEL_MESSAGE, { | ||||
|                 'MESSAGE': `GNOME Shell started at ${_startDate}`, | ||||
|                 'MESSAGE_ID': GNOMESHELL_STARTED_MESSAGE_ID | ||||
|                 'MESSAGE_ID': GNOMESHELL_STARTED_MESSAGE_ID, | ||||
|             }); | ||||
|         } | ||||
|  | ||||
| @@ -289,19 +289,19 @@ function _initializeUI() { | ||||
| function _getStylesheet(name) { | ||||
|     let stylesheet; | ||||
|  | ||||
|     stylesheet = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/' + name); | ||||
|     stylesheet = Gio.File.new_for_uri(`resource:///org/gnome/shell/theme/${name}`); | ||||
|     if (stylesheet.query_exists(null)) | ||||
|         return stylesheet; | ||||
|  | ||||
|     let dataDirs = GLib.get_system_data_dirs(); | ||||
|     for (let i = 0; i < dataDirs.length; i++) { | ||||
|         let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'theme', name]); | ||||
|         let stylesheet = Gio.file_new_for_path(path); | ||||
|         stylesheet = Gio.file_new_for_path(path); | ||||
|         if (stylesheet.query_exists(null)) | ||||
|             return stylesheet; | ||||
|     } | ||||
|  | ||||
|     stylesheet = Gio.File.new_for_path(global.datadir + '/theme/' + name); | ||||
|     stylesheet = Gio.File.new_for_path(`${global.datadir}/theme/${name}`); | ||||
|     if (stylesheet.query_exists(null)) | ||||
|         return stylesheet; | ||||
|  | ||||
| @@ -337,7 +337,7 @@ function _loadDefaultStylesheet() { | ||||
|  * | ||||
|  * Get the theme CSS file that the shell will load | ||||
|  * | ||||
|  * Returns: A #GFile that contains the theme CSS, | ||||
|  * @returns {?Gio.File}: A #GFile that contains the theme CSS, | ||||
|  *          null if using the default | ||||
|  */ | ||||
| function getThemeStylesheet() { | ||||
| @@ -346,8 +346,8 @@ function getThemeStylesheet() { | ||||
|  | ||||
| /** | ||||
|  * setThemeStylesheet: | ||||
|  * @cssStylesheet: A file path that contains the theme CSS, | ||||
|  *                 set it to null to use the default | ||||
|  * @param {string=} cssStylesheet: A file path that contains the theme CSS, | ||||
|  *     set it to null to use the default | ||||
|  * | ||||
|  * Set the theme CSS file that the shell will load | ||||
|  */ | ||||
| @@ -359,12 +359,12 @@ function reloadThemeResource() { | ||||
|     if (_themeResource) | ||||
|         _themeResource._unregister(); | ||||
|  | ||||
|     _themeResource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource'); | ||||
|     _themeResource = Gio.Resource.load(`${global.datadir}/gnome-shell-theme.gresource`); | ||||
|     _themeResource._register(); | ||||
| } | ||||
|  | ||||
| function _loadOskLayouts() { | ||||
|     _oskResource = Gio.Resource.load(global.datadir + '/gnome-shell-osk-layouts.gresource'); | ||||
|     _oskResource = Gio.Resource.load(`${global.datadir}/gnome-shell-osk-layouts.gresource`); | ||||
|     _oskResource._register(); | ||||
| } | ||||
|  | ||||
| @@ -374,11 +374,13 @@ function _loadOskLayouts() { | ||||
|  * Reloads the theme CSS file | ||||
|  */ | ||||
| function loadTheme() { | ||||
|     let themeContext = St.ThemeContext.get_for_stage (global.stage); | ||||
|     let themeContext = St.ThemeContext.get_for_stage(global.stage); | ||||
|     let previousTheme = themeContext.get_theme(); | ||||
|  | ||||
|     let theme = new St.Theme ({ application_stylesheet: _cssStylesheet, | ||||
|                                 default_stylesheet: _defaultCssStylesheet }); | ||||
|     let theme = new St.Theme({ | ||||
|         application_stylesheet: _cssStylesheet, | ||||
|         default_stylesheet: _defaultCssStylesheet, | ||||
|     }); | ||||
|  | ||||
|     if (theme.default_stylesheet == null) | ||||
|         throw new Error("No valid stylesheet found for '%s'".format(sessionMode.stylesheetName)); | ||||
| @@ -390,26 +392,26 @@ function loadTheme() { | ||||
|             theme.load_stylesheet(customStylesheets[i]); | ||||
|     } | ||||
|  | ||||
|     themeContext.set_theme (theme); | ||||
|     themeContext.set_theme(theme); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * notify: | ||||
|  * @msg: A message | ||||
|  * @details: Additional information | ||||
|  * @param {string} msg: A message | ||||
|  * @param {string} details: Additional information | ||||
|  */ | ||||
| function notify(msg, details) { | ||||
|     let source = new MessageTray.SystemNotificationSource(); | ||||
|     messageTray.add(source); | ||||
|     let notification = new MessageTray.Notification(source, msg, details); | ||||
|     notification.setTransient(true); | ||||
|     source.notify(notification); | ||||
|     source.showNotification(notification); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * notifyError: | ||||
|  * @msg: An error message | ||||
|  * @details: Additional information | ||||
|  * @param {string} msg: An error message | ||||
|  * @param {string} details: Additional information | ||||
|  * | ||||
|  * See shell_global_notify_problem(). | ||||
|  */ | ||||
| @@ -433,8 +435,8 @@ function _findModal(actor) { | ||||
|  | ||||
| /** | ||||
|  * pushModal: | ||||
|  * @actor: #ClutterActor which will be given keyboard focus | ||||
|  * @params: optional parameters | ||||
|  * @param {Clutter.Actor} actor: actor which will be given keyboard focus | ||||
|  * @param {Object=} params: optional parameters | ||||
|  * | ||||
|  * Ensure we are in a mode where all keyboard and mouse input goes to | ||||
|  * the stage, and focus @actor. Multiple calls to this function act in | ||||
| @@ -457,7 +459,7 @@ function _findModal(actor) { | ||||
|  *                global keybindings; the default of NONE will filter | ||||
|  *                out all keybindings | ||||
|  * | ||||
|  * Returns: true iff we successfully acquired a grab or already had one | ||||
|  * @returns {bool}: true iff we successfully acquired a grab or already had one | ||||
|  */ | ||||
| function pushModal(actor, params) { | ||||
|     params = Params.parse(params, { timestamp: global.get_current_time(), | ||||
| @@ -488,11 +490,11 @@ function pushModal(actor, params) { | ||||
|                 modalActorFocusStack[index].prevFocus = null; | ||||
|         }); | ||||
|     } | ||||
|     modalActorFocusStack.push({ actor: actor, | ||||
|     modalActorFocusStack.push({ actor, | ||||
|                                 destroyId: actorDestroyId, | ||||
|                                 prevFocus: prevFocus, | ||||
|                                 prevFocusDestroyId: prevFocusDestroyId, | ||||
|                                 actionMode: actionMode }); | ||||
|                                 prevFocus, | ||||
|                                 prevFocusDestroyId, | ||||
|                                 actionMode }); | ||||
|  | ||||
|     actionMode = params.actionMode; | ||||
|     global.stage.set_key_focus(actor); | ||||
| @@ -501,8 +503,9 @@ function pushModal(actor, params) { | ||||
|  | ||||
| /** | ||||
|  * popModal: | ||||
|  * @actor: #ClutterActor passed to original invocation of pushModal() | ||||
|  * @timestamp: optional timestamp | ||||
|  * @param {Clutter.Actor} actor: the actor passed to original invocation | ||||
|  *     of pushModal() | ||||
|  * @param {number=} timestamp: optional timestamp | ||||
|  * | ||||
|  * Reverse the effect of pushModal(). If this invocation is undoing | ||||
|  * the topmost invocation, then the focus will be restored to the | ||||
| @@ -573,24 +576,24 @@ function popModal(actor, timestamp) { | ||||
| } | ||||
|  | ||||
| function createLookingGlass() { | ||||
|     if (lookingGlass == null) { | ||||
|     if (lookingGlass == null) | ||||
|         lookingGlass = new LookingGlass.LookingGlass(); | ||||
|     } | ||||
|  | ||||
|     return lookingGlass; | ||||
| } | ||||
|  | ||||
| function openRunDialog() { | ||||
|     if (runDialog == null) { | ||||
|     if (runDialog == null) | ||||
|         runDialog = new RunDialog.RunDialog(); | ||||
|     } | ||||
|  | ||||
|     runDialog.open(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * activateWindow: | ||||
|  * @window: the Meta.Window to activate | ||||
|  * @time: (optional) current event time | ||||
|  * @workspaceNum: (optional) window's workspace number | ||||
|  * @param {Meta.Window} window: the window to activate | ||||
|  * @param {number=} time: current event time | ||||
|  * @param {number=} workspaceNum:  window's workspace number | ||||
|  * | ||||
|  * Activates @window, switching to its workspace first if necessary, | ||||
|  * and switching out of the overview if it's currently active | ||||
| @@ -598,7 +601,7 @@ function openRunDialog() { | ||||
| function activateWindow(window, time, workspaceNum) { | ||||
|     let workspaceManager = global.workspace_manager; | ||||
|     let activeWorkspaceNum = workspaceManager.get_active_workspace_index(); | ||||
|     let windowWorkspaceNum = (workspaceNum !== undefined) ? workspaceNum : window.get_workspace().index(); | ||||
|     let windowWorkspaceNum = workspaceNum !== undefined ? workspaceNum : window.get_workspace().index(); | ||||
|  | ||||
|     if (!time) | ||||
|         time = global.get_current_time(); | ||||
| @@ -666,8 +669,8 @@ function _queueBeforeRedraw(workId) { | ||||
|  | ||||
| /** | ||||
|  * initializeDeferredWork: | ||||
|  * @actor: A #ClutterActor | ||||
|  * @callback: Function to invoke to perform work | ||||
|  * @param {Clutter.Actor} actor: an actor | ||||
|  * @param {callback} callback: Function to invoke to perform work | ||||
|  * | ||||
|  * This function sets up a callback to be invoked when either the | ||||
|  * given actor is mapped, or after some period of time when the machine | ||||
| @@ -680,13 +683,13 @@ function _queueBeforeRedraw(workId) { | ||||
|  * initialization as well, under the assumption that new actors | ||||
|  * will need it. | ||||
|  * | ||||
|  * Returns: A string work identifier | ||||
|  * @returns {string}: A string work identifier | ||||
|  */ | ||||
| function initializeDeferredWork(actor, callback) { | ||||
|     // Turn into a string so we can use as an object property | ||||
|     let workId = `${(++_deferredWorkSequence)}`; | ||||
|     _deferredWorkData[workId] = { 'actor': actor, | ||||
|                                   'callback': callback }; | ||||
|     let workId = `${++_deferredWorkSequence}`; | ||||
|     _deferredWorkData[workId] = { actor, | ||||
|                                   callback }; | ||||
|     actor.connect('notify::mapped', () => { | ||||
|         if (!(actor.mapped && _deferredWorkQueue.includes(workId))) | ||||
|             return; | ||||
| @@ -704,7 +707,7 @@ function initializeDeferredWork(actor, callback) { | ||||
|  | ||||
| /** | ||||
|  * queueDeferredWork: | ||||
|  * @workId: work identifier | ||||
|  * @param {string} workId: work identifier | ||||
|  * | ||||
|  * Ensure that the work identified by @workId will be | ||||
|  * run on map or timeout. You should call this function | ||||
| @@ -740,12 +743,13 @@ class RestartMessage extends ModalDialog.ModalDialog { | ||||
|                       shouldFadeIn: false, | ||||
|                       destroyOnClose: true }); | ||||
|  | ||||
|         let label = new St.Label({ text: message }); | ||||
|         let label = new St.Label({ | ||||
|             text: message, | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|  | ||||
|         this.contentLayout.add(label, { x_fill: false, | ||||
|                                         y_fill: false, | ||||
|                                         x_align: St.Align.MIDDLE, | ||||
|                                         y_align: St.Align.MIDDLE }); | ||||
|         this.contentLayout.add_child(label); | ||||
|         this.buttonLayout.hide(); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| const { Atk, Clutter, Gio, GLib, GObject, Meta, Pango, St } = imports.gi; | ||||
| /* exported MessageListSection */ | ||||
| const { Atk, Clutter, Gio, GLib, | ||||
|         GObject, Graphene, Meta, Pango, St } = imports.gi; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Calendar = imports.ui.calendar; | ||||
| const Util = imports.misc.util; | ||||
| @@ -31,13 +32,18 @@ function _fixMarkup(text, allowMarkup) { | ||||
|     return GLib.markup_escape_text(text, -1); | ||||
| } | ||||
|  | ||||
| var URLHighlighter = class URLHighlighter { | ||||
|     constructor(text = '', lineWrap, allowMarkup) { | ||||
|         this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter', | ||||
|                                     x_expand: true, x_align: Clutter.ActorAlign.START }); | ||||
| var URLHighlighter = GObject.registerClass( | ||||
| class URLHighlighter extends St.Label { | ||||
|     _init(text = '', lineWrap, allowMarkup) { | ||||
|         super._init({ | ||||
|             reactive: true, | ||||
|             style_class: 'url-highlighter', | ||||
|             x_expand: true, | ||||
|             x_align: Clutter.ActorAlign.START, | ||||
|         }); | ||||
|         this._linkColor = '#ccccff'; | ||||
|         this.actor.connect('style-changed', () => { | ||||
|             let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false); | ||||
|         this.connect('style-changed', () => { | ||||
|             let [hasColor, color] = this.get_theme_node().lookup_color('link-color', false); | ||||
|             if (hasColor) { | ||||
|                 let linkColor = color.to_string().substr(0, 7); | ||||
|                 if (linkColor != this._linkColor) { | ||||
| @@ -46,70 +52,75 @@ var URLHighlighter = class URLHighlighter { | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         this.actor.clutter_text.line_wrap = lineWrap; | ||||
|         this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; | ||||
|         this.clutter_text.line_wrap = lineWrap; | ||||
|         this.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; | ||||
|  | ||||
|         this.setMarkup(text, allowMarkup); | ||||
|         this.actor.connect('button-press-event', (actor, event) => { | ||||
|             // Don't try to URL highlight when invisible. | ||||
|             // The MessageTray doesn't actually hide us, so | ||||
|             // we need to check for paint opacities as well. | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|             // Keep Notification.actor from seeing this and taking | ||||
|             // a pointer grab, which would block our button-release-event | ||||
|             // handler, if an URL is clicked | ||||
|             return this._findUrlAtPos(event) != -1; | ||||
|         }); | ||||
|         this.actor.connect('button-release-event', (actor, event) => { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1) { | ||||
|                 let url = this._urls[urlId].url; | ||||
|                 if (!url.includes(':')) | ||||
|                     url = 'http://' + url; | ||||
|  | ||||
|                 Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1)); | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|     vfunc_button_press_event(buttonEvent) { | ||||
|         // Don't try to URL highlight when invisible. | ||||
|         // The MessageTray doesn't actually hide us, so | ||||
|         // we need to check for paint opacities as well. | ||||
|         if (!this.visible || this.get_paint_opacity() == 0) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         }); | ||||
|         this.actor.connect('motion-event', (actor, event) => { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1 && !this._cursorChanged) { | ||||
|                 global.display.set_cursor(Meta.Cursor.POINTING_HAND); | ||||
|                 this._cursorChanged = true; | ||||
|             } else if (urlId == -1) { | ||||
|                 global.display.set_cursor(Meta.Cursor.DEFAULT); | ||||
|                 this._cursorChanged = false; | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         }); | ||||
|         this.actor.connect('leave-event', () => { | ||||
|             if (!this.actor.visible || this.actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|         // Keep Notification from seeing this and taking | ||||
|         // a pointer grab, which would block our button-release-event | ||||
|         // handler, if an URL is clicked | ||||
|         return this._findUrlAtPos(buttonEvent) != -1; | ||||
|     } | ||||
|  | ||||
|             if (this._cursorChanged) { | ||||
|                 this._cursorChanged = false; | ||||
|                 global.display.set_cursor(Meta.Cursor.DEFAULT); | ||||
|             } | ||||
|     vfunc_button_release_event(buttonEvent) { | ||||
|         if (!this.visible || this.get_paint_opacity() == 0) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         }); | ||||
|  | ||||
|         let urlId = this._findUrlAtPos(buttonEvent); | ||||
|         if (urlId != -1) { | ||||
|             let url = this._urls[urlId].url; | ||||
|             if (!url.includes(':')) | ||||
|                 url = `http://${url}`; | ||||
|  | ||||
|             Gio.app_info_launch_default_for_uri( | ||||
|                 url, global.create_app_launch_context(0, -1)); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     vfunc_motion_event(motionEvent) { | ||||
|         if (!this.visible || this.get_paint_opacity() == 0) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let urlId = this._findUrlAtPos(motionEvent); | ||||
|         if (urlId != -1 && !this._cursorChanged) { | ||||
|             global.display.set_cursor(Meta.Cursor.POINTING_HAND); | ||||
|             this._cursorChanged = true; | ||||
|         } else if (urlId == -1) { | ||||
|             global.display.set_cursor(Meta.Cursor.DEFAULT); | ||||
|             this._cursorChanged = false; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     vfunc_leave_event(crossingEvent) { | ||||
|         if (!this.visible || this.get_paint_opacity() == 0) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (this._cursorChanged) { | ||||
|             this._cursorChanged = false; | ||||
|             global.display.set_cursor(Meta.Cursor.DEFAULT); | ||||
|         } | ||||
|         return super.vfunc_leave_event(crossingEvent); | ||||
|     } | ||||
|  | ||||
|     setMarkup(text, allowMarkup) { | ||||
|         text = text ? _fixMarkup(text, allowMarkup) : ''; | ||||
|         this._text = text; | ||||
|  | ||||
|         this.actor.clutter_text.set_markup(text); | ||||
|         this.clutter_text.set_markup(text); | ||||
|         /* clutter_text.text contain text without markup */ | ||||
|         this._urls = Util.findUrls(this.actor.clutter_text.text); | ||||
|         this._urls = Util.findUrls(this.clutter_text.text); | ||||
|         this._highlightUrls(); | ||||
|     } | ||||
|  | ||||
| @@ -121,33 +132,33 @@ var URLHighlighter = class URLHighlighter { | ||||
|         for (let i = 0; i < urls.length; i++) { | ||||
|             let url = urls[i]; | ||||
|             let str = this._text.substr(pos, url.pos - pos); | ||||
|             markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>'; | ||||
|             markup += `${str}<span foreground="${this._linkColor}"><u>${url.url}</u></span>`; | ||||
|             pos = url.pos + url.url.length; | ||||
|         } | ||||
|         markup += this._text.substr(pos); | ||||
|         this.actor.clutter_text.set_markup(markup); | ||||
|         this.clutter_text.set_markup(markup); | ||||
|     } | ||||
|  | ||||
|     _findUrlAtPos(event) { | ||||
|         let success_; | ||||
|         let [x, y] = event.get_coords(); | ||||
|         [success_, x, y] = this.actor.transform_stage_point(x, y); | ||||
|         let { x, y } = event; | ||||
|         [, x, y] = this.transform_stage_point(x, y); | ||||
|         let findPos = -1; | ||||
|         for (let i = 0; i < this.actor.clutter_text.text.length; i++) { | ||||
|             let [success_, px, py, lineHeight] = this.actor.clutter_text.position_to_coords(i); | ||||
|         for (let i = 0; i < this.clutter_text.text.length; i++) { | ||||
|             let [, px, py, lineHeight] = this.clutter_text.position_to_coords(i); | ||||
|             if (py > y || py + lineHeight < y || x < px) | ||||
|                 continue; | ||||
|             findPos = i; | ||||
|         } | ||||
|         if (findPos != -1) { | ||||
|             for (let i = 0; i < this._urls.length; i++) | ||||
|             for (let i = 0; i < this._urls.length; i++) { | ||||
|                 if (findPos >= this._urls[i].pos && | ||||
|                     this._urls[i].pos + this._urls[i].url.length > findPos) | ||||
|                     return i; | ||||
|             } | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var ScaleLayout = GObject.registerClass( | ||||
| class ScaleLayout extends Clutter.BinLayout { | ||||
| @@ -160,20 +171,22 @@ class ScaleLayout extends Clutter.BinLayout { | ||||
|         if (this._container == container) | ||||
|             return; | ||||
|  | ||||
|         if (this._container) | ||||
|         if (this._container) { | ||||
|             for (let id of this._signals) | ||||
|                 this._container.disconnect(id); | ||||
|         } | ||||
|  | ||||
|         this._container = container; | ||||
|         this._signals = []; | ||||
|  | ||||
|         if (this._container) | ||||
|         if (this._container) { | ||||
|             for (let signal of ['notify::scale-x', 'notify::scale-y']) { | ||||
|                 let id = this._container.connect(signal, () => { | ||||
|                     this.layout_changed(); | ||||
|                 }); | ||||
|                 this._signals.push(id); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     vfunc_get_preferred_width(container, forHeight) { | ||||
| @@ -200,7 +213,7 @@ var LabelExpanderLayout = GObject.registerClass({ | ||||
|                                               'Expansion of the layout, between 0 (collapsed) ' + | ||||
|                                               'and 1 (fully expanded', | ||||
|                                               GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, | ||||
|                                               0, 1, 0) | ||||
|                                               0, 1, 0), | ||||
|     }, | ||||
| }, class LabelExpanderLayout extends Clutter.LayoutManager { | ||||
|     _init(params) { | ||||
| @@ -222,7 +235,7 @@ var LabelExpanderLayout = GObject.registerClass({ | ||||
|  | ||||
|         let visibleIndex = this._expansion > 0 ? 1 : 0; | ||||
|         for (let i = 0; this._container && i < this._container.get_n_children(); i++) | ||||
|             this._container.get_child_at_index(i).visible = (i == visibleIndex); | ||||
|             this._container.get_child_at_index(i).visible = i == visibleIndex; | ||||
|  | ||||
|         this.layout_changed(); | ||||
|     } | ||||
| @@ -283,21 +296,31 @@ var LabelExpanderLayout = GObject.registerClass({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var Message = class Message { | ||||
|     constructor(title, body) { | ||||
|         this.expanded = false; | ||||
|  | ||||
| var Message = GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'close': {}, | ||||
|         'expanded': {}, | ||||
|         'unexpanded': {}, | ||||
|     }, | ||||
| }, class Message extends St.Button { | ||||
|     _init(title, body) { | ||||
|         super._init({ | ||||
|             style_class: 'message', | ||||
|             accessible_role: Atk.Role.NOTIFICATION, | ||||
|             can_focus: true, | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|  | ||||
|         this.expanded = false; | ||||
|         this._useBodyMarkup = false; | ||||
|  | ||||
|         this.actor = new St.Button({ style_class: 'message', | ||||
|                                      accessible_role: Atk.Role.NOTIFICATION, | ||||
|                                      can_focus: true, | ||||
|                                      x_expand: true, x_fill: true }); | ||||
|         this.actor.connect('key-press-event', | ||||
|                            this._onKeyPressed.bind(this)); | ||||
|  | ||||
|         let vbox = new St.BoxLayout({ vertical: true }); | ||||
|         this.actor.set_child(vbox); | ||||
|         let vbox = new St.BoxLayout({ | ||||
|             vertical: true, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         this.set_child(vbox); | ||||
|  | ||||
|         let hbox = new St.BoxLayout(); | ||||
|         vbox.add_actor(hbox); | ||||
| @@ -308,7 +331,7 @@ var Message = class Message { | ||||
|  | ||||
|         this._iconBin = new St.Bin({ style_class: 'message-icon-bin', | ||||
|                                      y_expand: true, | ||||
|                                      y_align: St.Align.START, | ||||
|                                      y_align: Clutter.ActorAlign.START, | ||||
|                                      visible: false }); | ||||
|         hbox.add_actor(this._iconBin); | ||||
|  | ||||
| @@ -326,14 +349,18 @@ var Message = class Message { | ||||
|         this.setTitle(title); | ||||
|         titleBox.add_actor(this.titleLabel); | ||||
|  | ||||
|         this._secondaryBin = new St.Bin({ style_class: 'message-secondary-bin', | ||||
|                                           x_expand: true, y_expand: true, | ||||
|                                           x_fill: true, y_fill: true }); | ||||
|         this._secondaryBin = new St.Bin({ | ||||
|             style_class: 'message-secondary-bin', | ||||
|             x_expand: true, y_expand: true, | ||||
|         }); | ||||
|         titleBox.add_actor(this._secondaryBin); | ||||
|  | ||||
|         let closeIcon = new St.Icon({ icon_name: 'window-close-symbolic', | ||||
|                                       icon_size: 16 }); | ||||
|         this._closeButton = new St.Button({ child: closeIcon, opacity: 0 }); | ||||
|         this._closeButton = new St.Button({ | ||||
|             style_class: 'message-close-button', | ||||
|             child: closeIcon, opacity: 0, | ||||
|         }); | ||||
|         titleBox.add_actor(this._closeButton); | ||||
|  | ||||
|         this._bodyStack = new St.Widget({ x_expand: true }); | ||||
| @@ -341,15 +368,14 @@ var Message = class Message { | ||||
|         contentBox.add_actor(this._bodyStack); | ||||
|  | ||||
|         this.bodyLabel = new URLHighlighter('', false, this._useBodyMarkup); | ||||
|         this.bodyLabel.actor.add_style_class_name('message-body'); | ||||
|         this._bodyStack.add_actor(this.bodyLabel.actor); | ||||
|         this.bodyLabel.add_style_class_name('message-body'); | ||||
|         this._bodyStack.add_actor(this.bodyLabel); | ||||
|         this.setBody(body); | ||||
|  | ||||
|         this._closeButton.connect('clicked', this.close.bind(this)); | ||||
|         let actorHoverId = this.actor.connect('notify::hover', this._sync.bind(this)); | ||||
|         this._closeButton.connect('destroy', this.actor.disconnect.bind(this.actor, actorHoverId)); | ||||
|         this.actor.connect('clicked', this._onClicked.bind(this)); | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|         let actorHoverId = this.connect('notify::hover', this._sync.bind(this)); | ||||
|         this._closeButton.connect('destroy', this.disconnect.bind(this, actorHoverId)); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this._sync(); | ||||
|     } | ||||
|  | ||||
| @@ -359,7 +385,7 @@ var Message = class Message { | ||||
|  | ||||
|     setIcon(actor) { | ||||
|         this._iconBin.child = actor; | ||||
|         this._iconBin.visible = (actor != null); | ||||
|         this._iconBin.visible = actor != null; | ||||
|     } | ||||
|  | ||||
|     setSecondaryActor(actor) { | ||||
| @@ -430,12 +456,12 @@ var Message = class Message { | ||||
|     expand(animate) { | ||||
|         this.expanded = true; | ||||
|  | ||||
|         this._actionBin.visible = (this._actionBin.get_n_children() > 0); | ||||
|         this._actionBin.visible = this._actionBin.get_n_children() > 0; | ||||
|  | ||||
|         if (this._bodyStack.get_n_children() < 2) { | ||||
|             this._expandedLabel = new URLHighlighter(this._bodyText, | ||||
|                                                      true, this._useBodyMarkup); | ||||
|             this.setExpandedBody(this._expandedLabel.actor); | ||||
|             this.setExpandedBody(this._expandedLabel); | ||||
|         } | ||||
|  | ||||
|         if (animate) { | ||||
| @@ -448,7 +474,7 @@ var Message = class Message { | ||||
|             this._actionBin.ease({ | ||||
|                 scale_y: 1, | ||||
|                 duration: MessageTray.ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|         } else { | ||||
|             this._bodyStack.layout_manager.expansion = 1; | ||||
| @@ -472,7 +498,7 @@ var Message = class Message { | ||||
|                 onComplete: () => { | ||||
|                     this._actionBin.hide(); | ||||
|                     this.expanded = false; | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|         } else { | ||||
|             this._bodyStack.layout_manager.expansion = 0; | ||||
| @@ -488,19 +514,16 @@ var Message = class Message { | ||||
|     } | ||||
|  | ||||
|     _sync() { | ||||
|         let visible = this.actor.hover && this.canClose(); | ||||
|         let visible = this.hover && this.canClose(); | ||||
|         this._closeButton.opacity = visible ? 255 : 0; | ||||
|         this._closeButton.reactive = visible; | ||||
|     } | ||||
|  | ||||
|     _onClicked() { | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
|     } | ||||
|  | ||||
|     _onKeyPressed(a, event) { | ||||
|         let keysym = event.get_key_symbol(); | ||||
|     vfunc_key_press_event(keyEvent) { | ||||
|         let keysym = keyEvent.keyval; | ||||
|  | ||||
|         if (keysym == Clutter.KEY_Delete || | ||||
|             keysym == Clutter.KEY_KP_Delete) { | ||||
| @@ -509,37 +532,66 @@ var Message = class Message { | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Message.prototype); | ||||
| }); | ||||
|  | ||||
| var MessageListSection = class MessageListSection { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ style_class: 'message-list-section', | ||||
|                                         clip_to_allocation: true, | ||||
|                                         x_expand: true, vertical: true }); | ||||
| var MessageListSection = GObject.registerClass({ | ||||
|     Properties: { | ||||
|         'can-clear': GObject.ParamSpec.boolean( | ||||
|             'can-clear', 'can-clear', 'can-clear', | ||||
|             GObject.ParamFlags.READABLE, | ||||
|             false), | ||||
|         'empty': GObject.ParamSpec.boolean( | ||||
|             'empty', 'empty', 'empty', | ||||
|             GObject.ParamFlags.READABLE, | ||||
|             true), | ||||
|     }, | ||||
|     Signals: { | ||||
|         'can-clear-changed': {}, | ||||
|         'empty-changed': {}, | ||||
|         'message-focused': { param_types: [Message.$gtype] }, | ||||
|     }, | ||||
| }, class MessageListSection extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             style_class: 'message-list-section', | ||||
|             clip_to_allocation: true, | ||||
|             vertical: true, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|  | ||||
|         this._list = new St.BoxLayout({ style_class: 'message-list-section-list', | ||||
|                                         vertical: true }); | ||||
|         this.actor.add_actor(this._list); | ||||
|         this.add_actor(this._list); | ||||
|  | ||||
|         this._list.connect('actor-added', this._sync.bind(this)); | ||||
|         this._list.connect('actor-removed', this._sync.bind(this)); | ||||
|  | ||||
|         let id = Main.sessionMode.connect('updated', | ||||
|                                           this._sync.bind(this)); | ||||
|         this.actor.connect('destroy', () => { | ||||
|         this.connect('destroy', () => { | ||||
|             Main.sessionMode.disconnect(id); | ||||
|         }); | ||||
|  | ||||
|         this._messages = new Map(); | ||||
|         this._date = new Date(); | ||||
|         this.empty = true; | ||||
|         this.canClear = false; | ||||
|         this._empty = true; | ||||
|         this._canClear = false; | ||||
|         this._sync(); | ||||
|     } | ||||
|  | ||||
|     _onKeyFocusIn(actor) { | ||||
|         this.emit('key-focus-in', actor); | ||||
|     get empty() { | ||||
|         return this._empty; | ||||
|     } | ||||
|  | ||||
|     get canClear() { | ||||
|         return this._canClear; | ||||
|     } | ||||
|  | ||||
|     get _messages() { | ||||
|         return this._list.get_children().map(i => i.child); | ||||
|     } | ||||
|  | ||||
|     _onKeyFocusIn(messageActor) { | ||||
|         this.emit('message-focused', messageActor); | ||||
|     } | ||||
|  | ||||
|     get allowed() { | ||||
| @@ -558,94 +610,94 @@ var MessageListSection = class MessageListSection { | ||||
|     } | ||||
|  | ||||
|     addMessageAtIndex(message, index, animate) { | ||||
|         let obj = { | ||||
|             container: null, | ||||
|             destroyId: 0, | ||||
|             keyFocusId: 0, | ||||
|             closeId: 0 | ||||
|         }; | ||||
|         let pivot = new Clutter.Point({ x: .5, y: .5 }); | ||||
|         let scale = animate ? 0 : 1; | ||||
|         obj.container = new St.Widget({ layout_manager: new ScaleLayout(), | ||||
|                                         pivot_point: pivot, | ||||
|                                         scale_x: scale, scale_y: scale }); | ||||
|         obj.keyFocusId = message.actor.connect('key-focus-in', | ||||
|             this._onKeyFocusIn.bind(this)); | ||||
|         obj.destroyId = message.actor.connect('destroy', () => { | ||||
|             this.removeMessage(message, false); | ||||
|         if (this._messages.includes(message)) | ||||
|             throw new Error('Message was already added previously'); | ||||
|  | ||||
|         let listItem = new St.Bin({ | ||||
|             child: message, | ||||
|             layout_manager: new ScaleLayout(), | ||||
|             pivot_point: new Graphene.Point({ x: .5, y: .5 }), | ||||
|         }); | ||||
|         obj.closeId = message.connect('close', () => { | ||||
|         listItem._connectionsIds = []; | ||||
|  | ||||
|         listItem._connectionsIds.push(message.connect('key-focus-in', | ||||
|             this._onKeyFocusIn.bind(this))); | ||||
|         listItem._connectionsIds.push(message.connect('close', () => { | ||||
|             this.removeMessage(message, true); | ||||
|         }); | ||||
|         })); | ||||
|         listItem._connectionsIds.push(message.connect('destroy', () => { | ||||
|             listItem._connectionsIds.forEach(id => message.disconnect(id)); | ||||
|             listItem.destroy(); | ||||
|         })); | ||||
|  | ||||
|         this._messages.set(message, obj); | ||||
|         obj.container.add_actor(message.actor); | ||||
|         this._list.insert_child_at_index(listItem, index); | ||||
|  | ||||
|         this._list.insert_child_at_index(obj.container, index); | ||||
|  | ||||
|         if (animate) | ||||
|             obj.container.ease({ | ||||
|         if (animate) { | ||||
|             listItem.set({ scale_x: 0, scale_y: 0 }); | ||||
|             listItem.ease({ | ||||
|                 scale_x: 1, | ||||
|                 scale_y: 1, | ||||
|                 duration: MESSAGE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     moveMessage(message, index, animate) { | ||||
|         let obj = this._messages.get(message); | ||||
|         if (!this._messages.includes(message)) | ||||
|             throw new Error(`Impossible to move untracked message`); | ||||
|  | ||||
|         let listItem = message.get_parent(); | ||||
|  | ||||
|         if (!animate) { | ||||
|             this._list.set_child_at_index(obj.container, index); | ||||
|             this._list.set_child_at_index(listItem, index); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let onComplete = () => { | ||||
|             this._list.set_child_at_index(obj.container, index); | ||||
|             obj.container.ease({ | ||||
|             this._list.set_child_at_index(listItem, index); | ||||
|             listItem.ease({ | ||||
|                 scale_x: 1, | ||||
|                 scale_y: 1, | ||||
|                 duration: MESSAGE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|         }; | ||||
|         obj.container.ease({ | ||||
|         listItem.ease({ | ||||
|             scale_x: 0, | ||||
|             scale_y: 0, | ||||
|             duration: MESSAGE_ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete | ||||
|             onComplete, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     removeMessage(message, animate) { | ||||
|         let obj = this._messages.get(message); | ||||
|         if (!this._messages.includes(message)) | ||||
|             throw new Error(`Impossible to remove untracked message`); | ||||
|  | ||||
|         message.actor.disconnect(obj.destroyId); | ||||
|         message.actor.disconnect(obj.keyFocusId); | ||||
|         message.disconnect(obj.closeId); | ||||
|  | ||||
|         this._messages.delete(message); | ||||
|         let listItem = message.get_parent(); | ||||
|         listItem._connectionsIds.forEach(id => message.disconnect(id)); | ||||
|  | ||||
|         if (animate) { | ||||
|             obj.container.ease({ | ||||
|             listItem.ease({ | ||||
|                 scale_x: 0, | ||||
|                 scale_y: 0, | ||||
|                 duration: MESSAGE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 onComplete: () => { | ||||
|                     obj.container.destroy(); | ||||
|                     listItem.destroy(); | ||||
|                     global.sync_pointer(); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|         } else { | ||||
|             obj.container.destroy(); | ||||
|             listItem.destroy(); | ||||
|             global.sync_pointer(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     clear() { | ||||
|         let messages = [...this._messages.keys()].filter(msg => msg.canClose()); | ||||
|         let messages = this._messages.filter(msg => msg.canClose()); | ||||
|  | ||||
|         // If there are few messages, letting them all zoom out looks OK | ||||
|         if (messages.length < 2) { | ||||
| @@ -658,46 +710,37 @@ var MessageListSection = class MessageListSection { | ||||
|             let delay = MESSAGE_ANIMATION_TIME / Math.max(messages.length, 5); | ||||
|             for (let i = 0; i < messages.length; i++) { | ||||
|                 let message = messages[i]; | ||||
|                 let obj = this._messages.get(message); | ||||
|                 obj.container.ease({ | ||||
|                     anchor_x: this._list.width, | ||||
|                 message.get_parent().ease({ | ||||
|                     translation_x: this._list.width, | ||||
|                     opacity: 0, | ||||
|                     duration: MESSAGE_ANIMATION_TIME, | ||||
|                     delay: i * delay, | ||||
|                     mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                     onComplete: () => message.close() | ||||
|                     onComplete: () => message.close(), | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _canClear() { | ||||
|         for (let message of this._messages.keys()) | ||||
|             if (message.canClose()) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     _shouldShow() { | ||||
|         return !this.empty; | ||||
|     } | ||||
|  | ||||
|     _sync() { | ||||
|         let empty = this._list.get_n_children() == 0; | ||||
|         let changed = this.empty !== empty; | ||||
|         this.empty = empty; | ||||
|         let messages = this._messages; | ||||
|         let empty = messages.length == 0; | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('empty-changed'); | ||||
|         if (this._empty != empty) { | ||||
|             this._empty = empty; | ||||
|             this.notify('empty'); | ||||
|         } | ||||
|  | ||||
|         let canClear = this._canClear(); | ||||
|         changed = this.canClear !== canClear; | ||||
|         this.canClear = canClear; | ||||
|         let canClear = messages.some(m => m.canClose()); | ||||
|         if (this._canClear != canClear) { | ||||
|             this._canClear = canClear; | ||||
|             this.notify('can-clear'); | ||||
|         } | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('can-clear-changed'); | ||||
|  | ||||
|         this.actor.visible = this.allowed && this._shouldShow(); | ||||
|         this.visible = this.allowed && this._shouldShow(); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(MessageListSection.prototype); | ||||
| }); | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
|    SystemNotificationSource, MessageTray */ | ||||
|  | ||||
| const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Calendar = imports.ui.calendar; | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| @@ -34,7 +33,7 @@ var State = { | ||||
|     HIDDEN:  0, | ||||
|     SHOWING: 1, | ||||
|     SHOWN:   2, | ||||
|     HIDING:  3 | ||||
|     HIDING:  3, | ||||
| }; | ||||
|  | ||||
| // These reasons are useful when we destroy the notifications received through | ||||
| @@ -48,7 +47,7 @@ var NotificationDestroyedReason = { | ||||
|     EXPIRED: 1, | ||||
|     DISMISSED: 2, | ||||
|     SOURCE_CLOSED: 3, | ||||
|     REPLACED: 4 | ||||
|     REPLACED: 4, | ||||
| }; | ||||
|  | ||||
| // Message tray has its custom Urgency enumeration. LOW, NORMAL and CRITICAL | ||||
| @@ -59,7 +58,7 @@ var Urgency = { | ||||
|     LOW: 0, | ||||
|     NORMAL: 1, | ||||
|     HIGH: 2, | ||||
|     CRITICAL: 3 | ||||
|     CRITICAL: 3, | ||||
| }; | ||||
|  | ||||
| // The privacy of the details of a notification. USER is for notifications which | ||||
| @@ -134,72 +133,82 @@ var FocusGrabber = class FocusGrabber { | ||||
| // source, such as whether to play sound or honour the critical bit. | ||||
| // | ||||
| // A notification without a policy object will inherit the default one. | ||||
| var NotificationPolicy = class NotificationPolicy { | ||||
|     constructor(params) { | ||||
|         params = Params.parse(params, { | ||||
|             enable: true, | ||||
|             enableSound: true, | ||||
|             showBanners: true, | ||||
|             forceExpanded: false, | ||||
|             showInLockScreen: true, | ||||
|             detailsInLockScreen: false, | ||||
|         }); | ||||
|         Object.getOwnPropertyNames(params).forEach(key => { | ||||
|             let desc = Object.getOwnPropertyDescriptor(params, key); | ||||
|             Object.defineProperty(this, `_${key}`, desc); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| var NotificationPolicy = GObject.registerClass({ | ||||
|     Properties: { | ||||
|         'enable': GObject.ParamSpec.boolean( | ||||
|             'enable', 'enable', 'enable', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             true), | ||||
|         'enable-sound': GObject.ParamSpec.boolean( | ||||
|             'enable-sound', 'enable-sound', 'enable-sound', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             true), | ||||
|         'show-banners': GObject.ParamSpec.boolean( | ||||
|             'show-banners', 'show-banners', 'show-banners', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             true), | ||||
|         'force-expanded': GObject.ParamSpec.boolean( | ||||
|             'force-expanded', 'force-expanded', 'force-expanded', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             false), | ||||
|         'show-in-lock-screen': GObject.ParamSpec.boolean( | ||||
|             'show-in-lock-screen', 'show-in-lock-screen', 'show-in-lock-screen', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             false), | ||||
|         'details-in-lock-screen': GObject.ParamSpec.boolean( | ||||
|             'details-in-lock-screen', 'details-in-lock-screen', 'details-in-lock-screen', | ||||
|             GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|             false), | ||||
|     }, | ||||
| }, class NotificationPolicy extends GObject.Object { | ||||
|     // Do nothing for the default policy. These methods are only useful for the | ||||
|     // GSettings policy. | ||||
|     store() { } | ||||
|  | ||||
|     destroy() { } | ||||
|  | ||||
|     get enable() { | ||||
|         return this._enable; | ||||
|     destroy() { | ||||
|         this.run_dispose(); | ||||
|     } | ||||
|  | ||||
|     get enableSound() { | ||||
|         return this._enableSound; | ||||
|         return this.enable_sound; | ||||
|     } | ||||
|  | ||||
|     get showBanners() { | ||||
|         return this._showBanners; | ||||
|         return this.show_banners; | ||||
|     } | ||||
|  | ||||
|     get forceExpanded() { | ||||
|         return this._forceExpanded; | ||||
|         return this.force_expanded; | ||||
|     } | ||||
|  | ||||
|     get showInLockScreen() { | ||||
|         return this._showInLockScreen; | ||||
|         return this.show_in_lock_screen; | ||||
|     } | ||||
|  | ||||
|     get detailsInLockScreen() { | ||||
|         return this._detailsInLockScreen; | ||||
|         return this.details_in_lock_screen; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(NotificationPolicy.prototype); | ||||
| }); | ||||
|  | ||||
| var NotificationGenericPolicy = | ||||
| class NotificationGenericPolicy extends NotificationPolicy { | ||||
|     constructor() { | ||||
|         super(); | ||||
| var NotificationGenericPolicy = GObject.registerClass({ | ||||
| }, class NotificationGenericPolicy extends NotificationPolicy { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|         this.id = 'generic'; | ||||
|  | ||||
|         this._masterSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications' }); | ||||
|         this._masterSettings.connect('changed', this._changed.bind(this)); | ||||
|     } | ||||
|  | ||||
|     store() { } | ||||
|  | ||||
|     destroy() { | ||||
|         this._masterSettings.run_dispose(); | ||||
|  | ||||
|         super.destroy(); | ||||
|     } | ||||
|  | ||||
|     _changed(settings, key) { | ||||
|         this.emit('policy-changed', key); | ||||
|         if (this.constructor.find_property(key)) | ||||
|             this.notify(key); | ||||
|     } | ||||
|  | ||||
|     get showBanners() { | ||||
| @@ -209,12 +218,12 @@ class NotificationGenericPolicy extends NotificationPolicy { | ||||
|     get showInLockScreen() { | ||||
|         return this._masterSettings.get_boolean('show-in-lock-screen'); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var NotificationApplicationPolicy = | ||||
| class NotificationApplicationPolicy extends NotificationPolicy { | ||||
|     constructor(id) { | ||||
|         super(); | ||||
| var NotificationApplicationPolicy = GObject.registerClass({ | ||||
| }, class NotificationApplicationPolicy extends NotificationPolicy { | ||||
|     _init(id) { | ||||
|         super._init(); | ||||
|  | ||||
|         this.id = id; | ||||
|         this._canonicalId = this._canonicalizeId(id); | ||||
| @@ -240,12 +249,13 @@ class NotificationApplicationPolicy extends NotificationPolicy { | ||||
|     destroy() { | ||||
|         this._masterSettings.run_dispose(); | ||||
|         this._settings.run_dispose(); | ||||
|  | ||||
|         super.destroy(); | ||||
|     } | ||||
|  | ||||
|     _changed(settings, key) { | ||||
|         this.emit('policy-changed', key); | ||||
|         if (key == 'enable') | ||||
|             this.emit('enable-changed'); | ||||
|         if (this.constructor.find_property(key)) | ||||
|             this.notify(key); | ||||
|     } | ||||
|  | ||||
|     _canonicalizeId(id) { | ||||
| @@ -279,7 +289,7 @@ class NotificationApplicationPolicy extends NotificationPolicy { | ||||
|     get detailsInLockScreen() { | ||||
|         return this._settings.get_boolean('details-in-lock-screen'); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| // Notification: | ||||
| // @source: the notification's Source | ||||
| @@ -336,12 +346,25 @@ class NotificationApplicationPolicy extends NotificationPolicy { | ||||
| // @source allows playing sounds). | ||||
| // | ||||
| // [1] https://developer.gnome.org/notification-spec/#markup | ||||
| var Notification = class Notification { | ||||
|     constructor(source, title, banner, params) { | ||||
| var Notification = GObject.registerClass({ | ||||
|     Properties: { | ||||
|         'acknowledged': GObject.ParamSpec.boolean( | ||||
|             'acknowledged', 'acknowledged', 'acknowledged', | ||||
|             GObject.ParamFlags.READWRITE, | ||||
|             false), | ||||
|     }, | ||||
|     Signals: { | ||||
|         'activated': {}, | ||||
|         'destroy': { param_types: [GObject.TYPE_UINT] }, | ||||
|         'updated': { param_types: [GObject.TYPE_BOOLEAN] }, | ||||
|     }, | ||||
| }, class Notification extends GObject.Object { | ||||
|     _init(source, title, banner, params) { | ||||
|         super._init(); | ||||
|  | ||||
|         this.source = source; | ||||
|         this.title = title; | ||||
|         this.urgency = Urgency.NORMAL; | ||||
|         this.resident = false; | ||||
|         // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name | ||||
|         this.isTransient = false; | ||||
|         this.privacyScope = PrivacyScope.USER; | ||||
| @@ -353,6 +376,7 @@ var Notification = class Notification { | ||||
|         this._soundFile = null; | ||||
|         this._soundPlayed = false; | ||||
|         this.actions = []; | ||||
|         this.setResident(false); | ||||
|  | ||||
|         // If called with only one argument we assume the caller | ||||
|         // will call .update() later on. This is the case of | ||||
| @@ -411,7 +435,7 @@ var Notification = class Notification { | ||||
|     // @label: the label for the action's button | ||||
|     // @callback: the callback for the action | ||||
|     addAction(label, callback) { | ||||
|         this.actions.push({ label: label, callback: callback }); | ||||
|         this.actions.push({ label, callback }); | ||||
|     } | ||||
|  | ||||
|     get acknowledged() { | ||||
| @@ -422,7 +446,7 @@ var Notification = class Notification { | ||||
|         if (this._acknowledged == v) | ||||
|             return; | ||||
|         this._acknowledged = v; | ||||
|         this.emit('acknowledged-changed'); | ||||
|         this.notify('acknowledged'); | ||||
|     } | ||||
|  | ||||
|     setUrgency(urgency) { | ||||
| @@ -431,6 +455,15 @@ var Notification = class Notification { | ||||
|  | ||||
|     setResident(resident) { | ||||
|         this.resident = resident; | ||||
|  | ||||
|         if (this.resident) { | ||||
|             if (this._activatedId) { | ||||
|                 this.disconnect(this._activatedId); | ||||
|                 this._activatedId = 0; | ||||
|             } | ||||
|         } else if (!this._activatedId) { | ||||
|             this._activatedId = this.connect_after('activated', () => this.destroy()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     setTransient(isTransient) { | ||||
| @@ -472,23 +505,30 @@ var Notification = class Notification { | ||||
|  | ||||
|     activate() { | ||||
|         this.emit('activated'); | ||||
|         if (!this.resident) | ||||
|             this.destroy(); | ||||
|     } | ||||
|  | ||||
|     destroy(reason = NotificationDestroyedReason.DISMISSED) { | ||||
|         if (this._activatedId) { | ||||
|             this.disconnect(this._activatedId); | ||||
|             delete this._activatedId; | ||||
|         } | ||||
|  | ||||
|         this.emit('destroy', reason); | ||||
|         this.run_dispose(); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Notification.prototype); | ||||
| }); | ||||
|  | ||||
| var NotificationBanner = | ||||
| class NotificationBanner extends Calendar.NotificationMessage { | ||||
|     constructor(notification) { | ||||
|         super(notification); | ||||
| var NotificationBanner = GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'done-displaying': {}, | ||||
|         'unfocused': {}, | ||||
|     }, | ||||
| }, class NotificationBanner extends Calendar.NotificationMessage { | ||||
|     _init(notification) { | ||||
|         super._init(notification); | ||||
|  | ||||
|         this.actor.can_focus = false; | ||||
|         this.actor.add_style_class_name('notification-banner'); | ||||
|         this.can_focus = false; | ||||
|         this.add_style_class_name('notification-banner'); | ||||
|  | ||||
|         this._buttonBox = null; | ||||
|  | ||||
| @@ -569,13 +609,13 @@ class NotificationBanner extends Calendar.NotificationMessage { | ||||
|  | ||||
|     addAction(label, callback) { | ||||
|         let button = new St.Button({ style_class: 'notification-button', | ||||
|                                      label: label, | ||||
|                                      label, | ||||
|                                      x_expand: true, | ||||
|                                      can_focus: true }); | ||||
|  | ||||
|         return this.addButton(button, callback); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var SourceActor = GObject.registerClass( | ||||
| class SourceActor extends St.Widget { | ||||
| @@ -592,8 +632,7 @@ class SourceActor extends St.Widget { | ||||
|         this._actorDestroyed = false; | ||||
|  | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         this._iconBin = new St.Bin({ x_fill: true, | ||||
|                                      x_expand: true, | ||||
|         this._iconBin = new St.Bin({ x_expand: true, | ||||
|                                      height: size * scaleFactor, | ||||
|                                      width: size * scaleFactor }); | ||||
|  | ||||
| @@ -640,7 +679,7 @@ class SourceActorWithLabel extends SourceActor { | ||||
|  | ||||
|         this.add_actor(this._counterBin); | ||||
|  | ||||
|         this._countUpdatedId = this._source.connect('count-updated', this._updateCount.bind(this)); | ||||
|         this._countUpdatedId = this._source.connect('notify::count', this._updateCount.bind(this)); | ||||
|         this._updateCount(); | ||||
|  | ||||
|         this.connect('destroy', () => { | ||||
| @@ -688,11 +727,33 @@ class SourceActorWithLabel extends SourceActor { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var Source = class Source { | ||||
|     constructor(title, iconName) { | ||||
| var Source = GObject.registerClass({ | ||||
|     Properties: { | ||||
|         'count': GObject.ParamSpec.int( | ||||
|             'count', 'count', 'count', | ||||
|             GObject.ParamFlags.READABLE, | ||||
|             0, GLib.MAXINT32, 0), | ||||
|         'policy': GObject.ParamSpec.object( | ||||
|             'policy', 'policy', 'policy', | ||||
|             GObject.ParamFlags.READWRITE, | ||||
|             NotificationPolicy.$gtype), | ||||
|         'title': GObject.ParamSpec.string( | ||||
|             'title', 'title', 'title', | ||||
|             GObject.ParamFlags.READWRITE, | ||||
|             null), | ||||
|     }, | ||||
|     Signals: { | ||||
|         'destroy': { param_types: [GObject.TYPE_UINT] }, | ||||
|         'icon-updated': {}, | ||||
|         'notification-added': { param_types: [Notification.$gtype] }, | ||||
|         'notification-show': { param_types: [Notification.$gtype] }, | ||||
|     }, | ||||
| }, class Source extends GObject.Object { | ||||
|     _init(title, iconName) { | ||||
|         super._init({ title }); | ||||
|  | ||||
|         this.SOURCE_ICON_SIZE = 48; | ||||
|  | ||||
|         this.title = title; | ||||
|         this.iconName = iconName; | ||||
|  | ||||
|         this.isChat = false; | ||||
| @@ -727,7 +788,7 @@ var Source = class Source { | ||||
|     } | ||||
|  | ||||
|     countUpdated() { | ||||
|         this.emit('count-updated'); | ||||
|         super.notify('count'); | ||||
|     } | ||||
|  | ||||
|     _createPolicy() { | ||||
| @@ -741,8 +802,11 @@ var Source = class Source { | ||||
|     } | ||||
|  | ||||
|     setTitle(newTitle) { | ||||
|         if (this.title == newTitle) | ||||
|             return; | ||||
|  | ||||
|         this.title = newTitle; | ||||
|         this.emit('title-changed'); | ||||
|         this.notify('title'); | ||||
|     } | ||||
|  | ||||
|     createBanner(notification) { | ||||
| @@ -767,10 +831,10 @@ var Source = class Source { | ||||
|             return; | ||||
|  | ||||
|         this.notifications.splice(index, 1); | ||||
|         this.countUpdated(); | ||||
|  | ||||
|         if (this.notifications.length == 0) | ||||
|             this.destroy(); | ||||
|  | ||||
|         this.countUpdated(); | ||||
|     } | ||||
|  | ||||
|     pushNotification(notification) { | ||||
| @@ -781,22 +845,36 @@ var Source = class Source { | ||||
|             this.notifications.shift().destroy(NotificationDestroyedReason.EXPIRED); | ||||
|  | ||||
|         notification.connect('destroy', this._onNotificationDestroy.bind(this)); | ||||
|         notification.connect('acknowledged-changed', this.countUpdated.bind(this)); | ||||
|         notification.connect('notify::acknowledged', this.countUpdated.bind(this)); | ||||
|         this.notifications.push(notification); | ||||
|         this.emit('notification-added', notification); | ||||
|  | ||||
|         this.countUpdated(); | ||||
|     } | ||||
|  | ||||
|     notify(notification) { | ||||
|     showNotification(notification) { | ||||
|         notification.acknowledged = false; | ||||
|         this.pushNotification(notification); | ||||
|  | ||||
|         if (this.policy.showBanners || notification.urgency == Urgency.CRITICAL) { | ||||
|             this.emit('notify', notification); | ||||
|         } else { | ||||
|         if (this.policy.showBanners || notification.urgency == Urgency.CRITICAL) | ||||
|             this.emit('notification-show', notification); | ||||
|         else | ||||
|             notification.playSound(); | ||||
|     } | ||||
|  | ||||
|     notify(propName) { | ||||
|         if (propName instanceof Notification) { | ||||
|             try { | ||||
|                 throw new Error('Source.notify() has been moved to Source.showNotification()' + | ||||
|                                 'this code will break in the future'); | ||||
|             } catch (e) { | ||||
|                 logError(e); | ||||
|                 this.showNotification(propName); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         super.notify(propName); | ||||
|     } | ||||
|  | ||||
|     destroy(reason) { | ||||
| @@ -809,6 +887,8 @@ var Source = class Source { | ||||
|             notifications[i].destroy(reason); | ||||
|  | ||||
|         this.emit('destroy', reason); | ||||
|  | ||||
|         this.run_dispose(); | ||||
|     } | ||||
|  | ||||
|     iconUpdated() { | ||||
| @@ -820,17 +900,27 @@ var Source = class Source { | ||||
|     } | ||||
|  | ||||
|     destroyNonResidentNotifications() { | ||||
|         for (let i = this.notifications.length - 1; i >= 0; i--) | ||||
|         for (let i = this.notifications.length - 1; i >= 0; i--) { | ||||
|             if (!this.notifications[i].resident) | ||||
|                 this.notifications[i].destroy(); | ||||
|  | ||||
|         this.countUpdated(); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Source.prototype); | ||||
| }); | ||||
|  | ||||
| var MessageTray = GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'queue-changed': {}, | ||||
|         'source-added': { param_types: [Source.$gtype] }, | ||||
|         'source-removed': { param_types: [Source.$gtype] }, | ||||
|     }, | ||||
| }, class MessageTray extends St.Widget { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             visible: false, | ||||
|             clip_to_allocation: true, | ||||
|             layout_manager: new Clutter.BinLayout(), | ||||
|         }); | ||||
|  | ||||
| var MessageTray = class MessageTray { | ||||
|     constructor() { | ||||
|         this._presence = new GnomeSession.Presence((proxy, _error) => { | ||||
|             this._onStatusChanged(proxy.status); | ||||
|         }); | ||||
| @@ -847,18 +937,15 @@ var MessageTray = class MessageTray { | ||||
|             // so fix up Clutter's view of the pointer position in | ||||
|             // that case. | ||||
|             let related = ev.get_related(); | ||||
|             if (!related || this.actor.contains(related)) | ||||
|             if (!related || this.contains(related)) | ||||
|                 global.sync_pointer(); | ||||
|         }); | ||||
|  | ||||
|         this.actor = new St.Widget({ visible: false, | ||||
|                                      clip_to_allocation: true, | ||||
|                                      layout_manager: new Clutter.BinLayout() }); | ||||
|         let constraint = new Layout.MonitorConstraint({ primary: true }); | ||||
|         Main.layoutManager.panelBox.bind_property('visible', | ||||
|                                                   constraint, 'work-area', | ||||
|                                                   GObject.BindingFlags.SYNC_CREATE); | ||||
|         this.actor.add_constraint(constraint); | ||||
|         this.add_constraint(constraint); | ||||
|  | ||||
|         this._bannerBin = new St.Widget({ name: 'notification-container', | ||||
|                                           reactive: true, | ||||
| @@ -872,7 +959,7 @@ var MessageTray = class MessageTray { | ||||
|                                 this._onNotificationKeyRelease.bind(this)); | ||||
|         this._bannerBin.connect('notify::hover', | ||||
|                                 this._onNotificationHoverChanged.bind(this)); | ||||
|         this.actor.add_actor(this._bannerBin); | ||||
|         this.add_actor(this._bannerBin); | ||||
|  | ||||
|         this._notificationFocusGrabber = new FocusGrabber(this._bannerBin); | ||||
|         this._notificationQueue = []; | ||||
| @@ -901,7 +988,7 @@ var MessageTray = class MessageTray { | ||||
|         this._notificationTimeoutId = 0; | ||||
|         this._notificationRemoved = false; | ||||
|  | ||||
|         Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false }); | ||||
|         Main.layoutManager.addChrome(this, { affectsInputRegion: false }); | ||||
|         Main.layoutManager.trackChrome(this._bannerBin, { affectsInputRegion: true }); | ||||
|  | ||||
|         global.display.connect('in-fullscreen-changed', this._updateState.bind(this)); | ||||
| @@ -944,11 +1031,11 @@ var MessageTray = class MessageTray { | ||||
|     } | ||||
|  | ||||
|     _onDragBegin() { | ||||
|         Shell.util_set_hidden_from_pick(this.actor, true); | ||||
|         Shell.util_set_hidden_from_pick(this, true); | ||||
|     } | ||||
|  | ||||
|     _onDragEnd() { | ||||
|         Shell.util_set_hidden_from_pick(this.actor, false); | ||||
|         Shell.util_set_hidden_from_pick(this, false); | ||||
|     } | ||||
|  | ||||
|     get bannerAlignment() { | ||||
| @@ -997,22 +1084,22 @@ var MessageTray = class MessageTray { | ||||
|         // Register that we got a notification for this source | ||||
|         source.policy.store(); | ||||
|  | ||||
|         source.policy.connect('enable-changed', () => { | ||||
|         source.policy.connect('notify::enable', () => { | ||||
|             this._onSourceEnableChanged(source.policy, source); | ||||
|         }); | ||||
|         source.policy.connect('policy-changed', this._updateState.bind(this)); | ||||
|         source.policy.connect('notify', this._updateState.bind(this)); | ||||
|         this._onSourceEnableChanged(source.policy, source); | ||||
|     } | ||||
|  | ||||
|     _addSource(source) { | ||||
|         let obj = { | ||||
|             notifyId: 0, | ||||
|             showId: 0, | ||||
|             destroyId: 0, | ||||
|         }; | ||||
|  | ||||
|         this._sources.set(source, obj); | ||||
|  | ||||
|         obj.notifyId = source.connect('notify', this._onNotify.bind(this)); | ||||
|         obj.showId = source.connect('notification-show', this._onNotificationShow.bind(this)); | ||||
|         obj.destroyId = source.connect('destroy', this._onSourceDestroy.bind(this)); | ||||
|  | ||||
|         this.emit('source-added', source); | ||||
| @@ -1022,7 +1109,7 @@ var MessageTray = class MessageTray { | ||||
|         let obj = this._sources.get(source); | ||||
|         this._sources.delete(source); | ||||
|  | ||||
|         source.disconnect(obj.notifyId); | ||||
|         source.disconnect(obj.showId); | ||||
|         source.disconnect(obj.destroyId); | ||||
|  | ||||
|         this.emit('source-removed', source); | ||||
| @@ -1063,7 +1150,7 @@ var MessageTray = class MessageTray { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _onNotify(source, notification) { | ||||
|     _onNotificationShow(_source, notification) { | ||||
|         if (this._notification == notification) { | ||||
|             // If a notification that is being shown is updated, we update | ||||
|             // how it is shown and extend the time until it auto-hides. | ||||
| @@ -1075,7 +1162,7 @@ var MessageTray = class MessageTray { | ||||
|             // indicator in the panel; however do make an exception for CRITICAL | ||||
|             // notifications, as only banner mode allows expansion. | ||||
|             let bannerCount = this._notification ? 1 : 0; | ||||
|             let full = (this.queueCount + bannerCount >= MAX_NOTIFICATIONS_IN_QUEUE); | ||||
|             let full = this.queueCount + bannerCount >= MAX_NOTIFICATIONS_IN_QUEUE; | ||||
|             if (!full || notification.urgency == Urgency.CRITICAL) { | ||||
|                 notification.connect('destroy', | ||||
|                                      this._onNotificationDestroy.bind(this)); | ||||
| @@ -1195,7 +1282,7 @@ var MessageTray = class MessageTray { | ||||
|     // at the present time. | ||||
|     _updateState() { | ||||
|         let hasMonitor = Main.layoutManager.primaryMonitor != null; | ||||
|         this.actor.visible = !this._bannerBlocked && hasMonitor && this._banner != null; | ||||
|         this.visible = !this._bannerBlocked && hasMonitor && this._banner != null; | ||||
|         if (this._bannerBlocked || !hasMonitor) | ||||
|             return; | ||||
|  | ||||
| @@ -1222,7 +1309,7 @@ var MessageTray = class MessageTray { | ||||
|             let nextNotification = this._notificationQueue[0] || null; | ||||
|             if (hasNotifications && nextNotification) { | ||||
|                 let limited = this._busy || Main.layoutManager.primaryMonitor.inFullscreen; | ||||
|                 let showNextNotification = (!limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL); | ||||
|                 let showNextNotification = !limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL; | ||||
|                 if (showNextNotification) | ||||
|                     this._showNotification(); | ||||
|             } | ||||
| @@ -1232,7 +1319,7 @@ var MessageTray = class MessageTray { | ||||
|                            this._notification.urgency != Urgency.CRITICAL && | ||||
|                            !this._banner.focused && | ||||
|                            !this._pointerInNotification) || this._notificationExpired; | ||||
|             let mustClose = (this._notificationRemoved || !hasNotifications || expired); | ||||
|             let mustClose = this._notificationRemoved || !hasNotifications || expired; | ||||
|  | ||||
|             if (mustClose) { | ||||
|                 let animate = hasNotifications && !this._notificationRemoved; | ||||
| @@ -1275,11 +1362,11 @@ var MessageTray = class MessageTray { | ||||
|             this._updateState(); | ||||
|         }); | ||||
|  | ||||
|         this._bannerBin.add_actor(this._banner.actor); | ||||
|         this._bannerBin.add_actor(this._banner); | ||||
|  | ||||
|         this._bannerBin.opacity = 0; | ||||
|         this._bannerBin.y = -this._banner.actor.height; | ||||
|         this.actor.show(); | ||||
|         this._bannerBin.y = -this._banner.height; | ||||
|         this.show(); | ||||
|  | ||||
|         Meta.disable_unredirect_for_display(global.display); | ||||
|         this._updateShowingNotification(); | ||||
| @@ -1327,7 +1414,7 @@ var MessageTray = class MessageTray { | ||||
|         this._bannerBin.ease({ | ||||
|             opacity: 255, | ||||
|             duration: ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.LINEAR | ||||
|             mode: Clutter.AnimationMode.LINEAR, | ||||
|         }); | ||||
|         this._bannerBin.ease({ | ||||
|             y: 0, | ||||
| @@ -1337,7 +1424,7 @@ var MessageTray = class MessageTray { | ||||
|                 this._notificationState = State.SHOWN; | ||||
|                 this._showNotificationCompleted(); | ||||
|                 this._updateState(); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -1403,7 +1490,7 @@ var MessageTray = class MessageTray { | ||||
|             this._bannerBin.ease({ | ||||
|                 opacity: 0, | ||||
|                 duration: ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_BACK | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_BACK, | ||||
|             }); | ||||
|             this._bannerBin.ease({ | ||||
|                 y: -this._bannerBin.height, | ||||
| @@ -1413,7 +1500,7 @@ var MessageTray = class MessageTray { | ||||
|                     this._notificationState = State.HIDDEN; | ||||
|                     this._hideNotificationCompleted(); | ||||
|                     this._updateState(); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|         } else { | ||||
|             this._bannerBin.y = -this._bannerBin.height; | ||||
| @@ -1426,16 +1513,16 @@ var MessageTray = class MessageTray { | ||||
|     _hideNotificationCompleted() { | ||||
|         let notification = this._notification; | ||||
|         this._notification = null; | ||||
|         if (notification.isTransient) | ||||
|         if (!this._notificationRemoved && notification.isTransient) | ||||
|             notification.destroy(NotificationDestroyedReason.EXPIRED); | ||||
|  | ||||
|         this._pointerInNotification = false; | ||||
|         this._notificationRemoved = false; | ||||
|         Meta.enable_unredirect_for_display(global.display); | ||||
|  | ||||
|         this._banner.actor.destroy(); | ||||
|         this._banner.destroy(); | ||||
|         this._banner = null; | ||||
|         this.actor.hide(); | ||||
|         this.hide(); | ||||
|     } | ||||
|  | ||||
|     _expandActiveNotification() { | ||||
| @@ -1457,15 +1544,15 @@ var MessageTray = class MessageTray { | ||||
|     _ensureBannerFocused() { | ||||
|         this._notificationFocusGrabber.grabFocus(); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(MessageTray.prototype); | ||||
| }); | ||||
|  | ||||
| var SystemNotificationSource = class SystemNotificationSource extends Source { | ||||
|     constructor() { | ||||
|         super(_("System Information"), 'dialog-information-symbolic'); | ||||
| var SystemNotificationSource = GObject.registerClass( | ||||
| class SystemNotificationSource extends Source { | ||||
|     _init() { | ||||
|         super._init(_("System Information"), 'dialog-information-symbolic'); | ||||
|     } | ||||
|  | ||||
|     open() { | ||||
|         this.destroy(); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|   | ||||
| @@ -17,7 +17,7 @@ var State = { | ||||
|     CLOSED: 1, | ||||
|     OPENING: 2, | ||||
|     CLOSING: 3, | ||||
|     FADED_OUT: 4 | ||||
|     FADED_OUT: 4, | ||||
| }; | ||||
|  | ||||
| var ModalDialog = GObject.registerClass({ | ||||
| @@ -26,9 +26,9 @@ var ModalDialog = GObject.registerClass({ | ||||
|                                        GObject.ParamFlags.READABLE, | ||||
|                                        Math.min(...Object.values(State)), | ||||
|                                        Math.max(...Object.values(State)), | ||||
|                                        State.CLOSED) | ||||
|                                        State.CLOSED), | ||||
|     }, | ||||
|     Signals: { 'opened': {}, 'closed': {} } | ||||
|     Signals: { 'opened': {}, 'closed': {} }, | ||||
| }, class ModalDialog extends St.Widget { | ||||
|     _init(params) { | ||||
|         super._init({ visible: false, | ||||
| @@ -57,9 +57,12 @@ var ModalDialog = GObject.registerClass({ | ||||
|                                                       coordinate: Clutter.BindCoordinate.ALL }); | ||||
|         this.add_constraint(constraint); | ||||
|  | ||||
|         this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|         this._backgroundBin = new St.Bin({ child: this.backgroundStack, | ||||
|                                            x_fill: true, y_fill: true }); | ||||
|         this.backgroundStack = new St.Widget({ | ||||
|             layout_manager: new Clutter.BinLayout(), | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this._backgroundBin = new St.Bin({ child: this.backgroundStack }); | ||||
|         this._monitorConstraint = new Layout.MonitorConstraint(); | ||||
|         this._backgroundBin.add_constraint(this._monitorConstraint); | ||||
|         this.add_actor(this._backgroundBin); | ||||
| @@ -121,7 +124,7 @@ var ModalDialog = GObject.registerClass({ | ||||
|  | ||||
|         this.dialogLayout.opacity = 255; | ||||
|         if (this._lightbox) | ||||
|             this._lightbox.show(); | ||||
|             this._lightbox.lightOn(); | ||||
|         this.opacity = 0; | ||||
|         this.show(); | ||||
|         this.ease({ | ||||
| @@ -131,7 +134,7 @@ var ModalDialog = GObject.registerClass({ | ||||
|             onComplete: () => { | ||||
|                 this._setState(State.OPENED); | ||||
|                 this.emit('opened'); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -180,7 +183,7 @@ var ModalDialog = GObject.registerClass({ | ||||
|                 opacity: 0, | ||||
|                 duration: OPEN_AND_CLOSE_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 onComplete: () => this._closeComplete() | ||||
|                 onComplete: () => this._closeComplete(), | ||||
|             }); | ||||
|         } else { | ||||
|             this._closeComplete(); | ||||
| @@ -203,7 +206,7 @@ var ModalDialog = GObject.registerClass({ | ||||
|         this._hasModal = false; | ||||
|  | ||||
|         if (!this._shellReactive) | ||||
|             this._eventBlocker.raise_top(); | ||||
|             this.backgroundStack.set_child_above_sibling(this._eventBlocker, null); | ||||
|     } | ||||
|  | ||||
|     pushModal(timestamp) { | ||||
| @@ -216,6 +219,8 @@ var ModalDialog = GObject.registerClass({ | ||||
|         if (!Main.pushModal(this, params)) | ||||
|             return false; | ||||
|  | ||||
|         Main.layoutManager.emit('system-modal-opened'); | ||||
|  | ||||
|         this._hasModal = true; | ||||
|         if (this._savedKeyFocus) { | ||||
|             this._savedKeyFocus.grab_key_focus(); | ||||
| @@ -226,7 +231,7 @@ var ModalDialog = GObject.registerClass({ | ||||
|         } | ||||
|  | ||||
|         if (!this._shellReactive) | ||||
|             this._eventBlocker.lower_bottom(); | ||||
|             this.backgroundStack.set_child_below_sibling(this._eventBlocker, null); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| @@ -253,7 +258,7 @@ var ModalDialog = GObject.registerClass({ | ||||
|             opacity: 0, | ||||
|             duration: FADE_OUT_DIALOG_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => (this.state = State.FADED_OUT) | ||||
|             onComplete: () => (this.state = State.FADED_OUT), | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* exported MediaSection */ | ||||
| const { Gio, Shell, St } = imports.gi; | ||||
| const { Gio, GObject, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Calendar = imports.ui.calendar; | ||||
| @@ -19,9 +19,10 @@ const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface); | ||||
|  | ||||
| const MPRIS_PLAYER_PREFIX = 'org.mpris.MediaPlayer2.'; | ||||
|  | ||||
| var MediaMessage = class MediaMessage extends MessageList.Message { | ||||
|     constructor(player) { | ||||
|         super('', ''); | ||||
| var MediaMessage = GObject.registerClass( | ||||
| class MediaMessage extends MessageList.Message { | ||||
|     _init(player) { | ||||
|         super._init('', ''); | ||||
|  | ||||
|         this._player = player; | ||||
|  | ||||
| @@ -43,12 +44,20 @@ var MediaMessage = class MediaMessage extends MessageList.Message { | ||||
|                 this._player.next(); | ||||
|             }); | ||||
|  | ||||
|         this._player.connect('changed', this._update.bind(this)); | ||||
|         this._player.connect('closed', this.close.bind(this)); | ||||
|         this._updateHandlerId = | ||||
|             this._player.connect('changed', this._update.bind(this)); | ||||
|         this._closedHandlerId = | ||||
|             this._player.connect('closed', this.close.bind(this)); | ||||
|         this._update(); | ||||
|     } | ||||
|  | ||||
|     _onClicked() { | ||||
|     _onDestroy() { | ||||
|         super._onDestroy(); | ||||
|         this._player.disconnect(this._updateHandlerId); | ||||
|         this._player.disconnect(this._closedHandlerId); | ||||
|     } | ||||
|  | ||||
|     vfunc_clicked() { | ||||
|         this._player.raise(); | ||||
|         Main.panel.closeCalendar(); | ||||
|     } | ||||
| @@ -63,7 +72,7 @@ var MediaMessage = class MediaMessage extends MessageList.Message { | ||||
|  | ||||
|         if (this._player.trackCoverUrl) { | ||||
|             let file = Gio.File.new_for_uri(this._player.trackCoverUrl); | ||||
|             this._icon.gicon = new Gio.FileIcon({ file: file }); | ||||
|             this._icon.gicon = new Gio.FileIcon({ file }); | ||||
|             this._icon.remove_style_class_name('fallback'); | ||||
|         } else { | ||||
|             this._icon.icon_name = 'audio-x-generic-symbolic'; | ||||
| @@ -79,7 +88,7 @@ var MediaMessage = class MediaMessage extends MessageList.Message { | ||||
|         this._updateNavButton(this._prevButton, this._player.canGoPrevious); | ||||
|         this._updateNavButton(this._nextButton, this._player.canGoNext); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var MprisPlayer = class MprisPlayer { | ||||
|     constructor(busName) { | ||||
| @@ -94,6 +103,7 @@ var MprisPlayer = class MprisPlayer { | ||||
|         this._trackArtists = []; | ||||
|         this._trackTitle = ''; | ||||
|         this._trackCoverUrl = ''; | ||||
|         this._busName = busName; | ||||
|     } | ||||
|  | ||||
|     get status() { | ||||
| @@ -176,9 +186,39 @@ var MprisPlayer = class MprisPlayer { | ||||
|         for (let prop in this._playerProxy.Metadata) | ||||
|             metadata[prop] = this._playerProxy.Metadata[prop].deep_unpack(); | ||||
|  | ||||
|         this._trackArtists = metadata['xesam:artist'] || [_("Unknown artist")]; | ||||
|         this._trackTitle = metadata['xesam:title'] || _("Unknown title"); | ||||
|         this._trackCoverUrl = metadata['mpris:artUrl'] || ''; | ||||
|         // Validate according to the spec; some clients send buggy metadata: | ||||
|         // https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata | ||||
|         this._trackArtists = metadata['xesam:artist']; | ||||
|         if (!Array.isArray(this._trackArtists) || | ||||
|             !this._trackArtists.every(artist => typeof artist === 'string')) { | ||||
|             if (typeof this._trackArtists !== 'undefined') { | ||||
|                 log(`Received faulty track artist metadata from ${ | ||||
|                     this._busName}; expected an array of strings, got ${ | ||||
|                     this._trackArtists} (${typeof this._trackArtists})`); | ||||
|             } | ||||
|             this._trackArtists =  [_("Unknown artist")]; | ||||
|         } | ||||
|  | ||||
|         this._trackTitle = metadata['xesam:title']; | ||||
|         if (typeof this._trackTitle !== 'string') { | ||||
|             if (typeof this._trackTitle !== 'undefined') { | ||||
|                 log(`Received faulty track title metadata from ${ | ||||
|                     this._busName}; expected a string, got ${ | ||||
|                     this._trackTitle} (${typeof this._trackTitle})`); | ||||
|             } | ||||
|             this._trackTitle = _("Unknown title"); | ||||
|         } | ||||
|  | ||||
|         this._trackCoverUrl = metadata['mpris:artUrl']; | ||||
|         if (typeof this._trackCoverUrl !== 'string') { | ||||
|             if (typeof this._trackCoverUrl !== 'undefined') { | ||||
|                 log(`Received faulty track cover art metadata from ${ | ||||
|                     this._busName}; expected a string, got ${ | ||||
|                     this._trackCoverUrl} (${typeof this._trackCoverUrl})`); | ||||
|             } | ||||
|             this._trackCoverUrl = ''; | ||||
|         } | ||||
|  | ||||
|         this.emit('changed'); | ||||
|  | ||||
|         let visible = this._playerProxy.CanPlay; | ||||
| @@ -188,15 +228,16 @@ var MprisPlayer = class MprisPlayer { | ||||
|             if (visible) | ||||
|                 this.emit('show'); | ||||
|             else | ||||
|                 this._close(); | ||||
|                 this.emit('hide'); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(MprisPlayer.prototype); | ||||
|  | ||||
| var MediaSection = class MediaSection extends MessageList.MessageListSection { | ||||
|     constructor() { | ||||
|         super(); | ||||
| var MediaSection = GObject.registerClass( | ||||
| class MediaSection extends MessageList.MessageListSection { | ||||
|     _init() { | ||||
|         super._init(); | ||||
|  | ||||
|         this._players = new Map(); | ||||
|  | ||||
| @@ -215,15 +256,20 @@ var MediaSection = class MediaSection extends MessageList.MessageListSection { | ||||
|             return; | ||||
|  | ||||
|         let player = new MprisPlayer(busName); | ||||
|         let message = null; | ||||
|         player.connect('closed', | ||||
|             () => { | ||||
|                 this._players.delete(busName); | ||||
|             }); | ||||
|         player.connect('show', | ||||
|             () => { | ||||
|                 let message = new MediaMessage(player); | ||||
|                 this.addMessage(message, true); | ||||
|             }); | ||||
|         player.connect('show', () => { | ||||
|             message = new MediaMessage(player); | ||||
|             this.addMessage(message, true); | ||||
|         }); | ||||
|         player.connect('hide', () => { | ||||
|             this.removeMessage(message, true); | ||||
|             message = null; | ||||
|         }); | ||||
|  | ||||
|         this._players.set(busName, player); | ||||
|     } | ||||
|  | ||||
| @@ -247,4 +293,4 @@ var MediaSection = class MediaSection extends MessageList.MessageListSection { | ||||
|         if (newOwner && !oldOwner) | ||||
|             this._addPlayer(name); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported NotificationDaemon */ | ||||
|  | ||||
| const { GdkPixbuf, Gio, GLib, Shell, St } = imports.gi; | ||||
| const { GdkPixbuf, Gio, GLib, GObject, Shell, St } = imports.gi; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const Main = imports.ui.main; | ||||
| @@ -23,13 +23,13 @@ var NotificationClosedReason = { | ||||
|     EXPIRED: 1, | ||||
|     DISMISSED: 2, | ||||
|     APP_CLOSED: 3, | ||||
|     UNDEFINED: 4 | ||||
|     UNDEFINED: 4, | ||||
| }; | ||||
|  | ||||
| var Urgency = { | ||||
|     LOW: 0, | ||||
|     NORMAL: 1, | ||||
|     CRITICAL: 2 | ||||
|     CRITICAL: 2, | ||||
| }; | ||||
|  | ||||
| const rewriteRules = { | ||||
| @@ -39,8 +39,8 @@ const rewriteRules = { | ||||
|         { pattern:     /^XChat: New public message from: (\S*) \((.*)\)$/, | ||||
|           replacement: '$2 <$1>' }, | ||||
|         { pattern:     /^XChat: Highlighted message from: (\S*) \((.*)\)$/, | ||||
|           replacement: '$2 <$1>' } | ||||
|     ] | ||||
|           replacement: '$2 <$1>' }, | ||||
|     ], | ||||
| }; | ||||
|  | ||||
| var FdoNotificationDaemon = class FdoNotificationDaemon { | ||||
| @@ -201,13 +201,13 @@ var FdoNotificationDaemon = class FdoNotificationDaemon { | ||||
|                 hints['image-data'] = hints['icon_data']; | ||||
|         } | ||||
|  | ||||
|         let ndata = { appName: appName, | ||||
|                       icon: icon, | ||||
|                       summary: summary, | ||||
|                       body: body, | ||||
|                       actions: actions, | ||||
|                       hints: hints, | ||||
|                       timeout: timeout }; | ||||
|         let ndata = { appName, | ||||
|                       icon, | ||||
|                       summary, | ||||
|                       body, | ||||
|                       actions, | ||||
|                       hints, | ||||
|                       timeout }; | ||||
|         if (replacesId != 0 && this._notifications[replacesId]) { | ||||
|             ndata.id = id = replacesId; | ||||
|             ndata.notification = this._notifications[replacesId].notification; | ||||
| @@ -245,7 +245,7 @@ var FdoNotificationDaemon = class FdoNotificationDaemon { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let [pid] = result; | ||||
|             [pid] = result; | ||||
|             source = this._getSource(appName, pid, ndata, sender, null); | ||||
|  | ||||
|             this._senderToPid[sender] = pid; | ||||
| @@ -299,7 +299,7 @@ var FdoNotificationDaemon = class FdoNotificationDaemon { | ||||
|         else if (!gicon) | ||||
|             gicon = this._fallbackIconForNotificationData(hints); | ||||
|  | ||||
|         notification.update(summary, body, { gicon: gicon, | ||||
|         notification.update(summary, body, { gicon, | ||||
|                                              bannerMarkup: true, | ||||
|                                              clear: true, | ||||
|                                              soundFile: hints['sound-file'], | ||||
| @@ -310,12 +310,13 @@ var FdoNotificationDaemon = class FdoNotificationDaemon { | ||||
|         if (actions.length) { | ||||
|             for (let i = 0; i < actions.length - 1; i += 2) { | ||||
|                 let [actionId, label] = [actions[i], actions[i + 1]]; | ||||
|                 if (actionId == 'default') | ||||
|                 if (actionId == 'default') { | ||||
|                     hasDefaultAction = true; | ||||
|                 else | ||||
|                 } else { | ||||
|                     notification.addAction(label, () => { | ||||
|                         this._emitActionInvoked(ndata.id, actionId); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -345,7 +346,7 @@ var FdoNotificationDaemon = class FdoNotificationDaemon { | ||||
|         // of the 'transient' hint with hints['transient'] rather than hints.transient | ||||
|         notification.setTransient(!!hints['transient']); | ||||
|  | ||||
|         let privacyScope = (hints['x-gnome-privacy-scope'] || 'user'); | ||||
|         let privacyScope = hints['x-gnome-privacy-scope'] || 'user'; | ||||
|         notification.setPrivacyScope(privacyScope == 'system' | ||||
|             ? MessageTray.PrivacyScope.SYSTEM | ||||
|             : MessageTray.PrivacyScope.USER); | ||||
| @@ -383,7 +384,7 @@ var FdoNotificationDaemon = class FdoNotificationDaemon { | ||||
|             Config.PACKAGE_NAME, | ||||
|             'GNOME', | ||||
|             Config.PACKAGE_VERSION, | ||||
|             '1.2' | ||||
|             '1.2', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| @@ -412,10 +413,10 @@ var FdoNotificationDaemon = class FdoNotificationDaemon { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var FdoNotificationDaemonSource = | ||||
| var FdoNotificationDaemonSource = GObject.registerClass( | ||||
| class FdoNotificationDaemonSource extends MessageTray.Source { | ||||
|     constructor(title, pid, sender, appId) { | ||||
|         super(title); | ||||
|     _init(title, pid, sender, appId) { | ||||
|         super._init(title); | ||||
|  | ||||
|         this.pid = pid; | ||||
|         this.app = this._getApp(appId); | ||||
| @@ -427,13 +428,14 @@ class FdoNotificationDaemonSource extends MessageTray.Source { | ||||
|         else | ||||
|             this.useNotificationIcon = true; | ||||
|  | ||||
|         if (sender) | ||||
|         if (sender) { | ||||
|             this._nameWatcherId = Gio.DBus.session.watch_name(sender, | ||||
|                                                               Gio.BusNameWatcherFlags.NONE, | ||||
|                                                               null, | ||||
|                                                               this._onNameVanished.bind(this)); | ||||
|         else | ||||
|         } else { | ||||
|             this._nameWatcherId = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _createPolicy() { | ||||
| @@ -464,7 +466,7 @@ class FdoNotificationDaemonSource extends MessageTray.Source { | ||||
|         if (notification.resident && this.app && tracker.focus_app == this.app) | ||||
|             this.pushNotification(notification); | ||||
|         else | ||||
|             this.notify(notification); | ||||
|             this.showNotification(notification); | ||||
|     } | ||||
|  | ||||
|     _getApp(appId) { | ||||
| @@ -526,19 +528,19 @@ class FdoNotificationDaemonSource extends MessageTray.Source { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| const PRIORITY_URGENCY_MAP = { | ||||
|     low: MessageTray.Urgency.LOW, | ||||
|     normal: MessageTray.Urgency.NORMAL, | ||||
|     high: MessageTray.Urgency.HIGH, | ||||
|     urgent: MessageTray.Urgency.CRITICAL | ||||
|     urgent: MessageTray.Urgency.CRITICAL, | ||||
| }; | ||||
|  | ||||
| var GtkNotificationDaemonNotification = | ||||
| var GtkNotificationDaemonNotification = GObject.registerClass( | ||||
| class GtkNotificationDaemonNotification extends MessageTray.Notification { | ||||
|     constructor(source, notification) { | ||||
|         super(source); | ||||
|     _init(source, notification) { | ||||
|         super._init(source); | ||||
|         this._serialized = GLib.Variant.new('a{sv}', notification); | ||||
|  | ||||
|         let { title, | ||||
| @@ -602,7 +604,7 @@ class GtkNotificationDaemonNotification extends MessageTray.Notification { | ||||
|     serialize() { | ||||
|         return this._serialized; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| const FdoApplicationIface = loadInterfaceXML('org.freedesktop.Application'); | ||||
| const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface); | ||||
| @@ -618,9 +620,9 @@ function getPlatformData() { | ||||
|  | ||||
| function InvalidAppError() {} | ||||
|  | ||||
| var GtkNotificationDaemonAppSource = | ||||
| var GtkNotificationDaemonAppSource = GObject.registerClass( | ||||
| class GtkNotificationDaemonAppSource extends MessageTray.Source { | ||||
|     constructor(appId) { | ||||
|     _init(appId) { | ||||
|         let objectPath = objectPathFromAppId(appId); | ||||
|         if (!GLib.Variant.is_object_path(objectPath)) | ||||
|             throw new InvalidAppError(); | ||||
| @@ -629,7 +631,7 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source { | ||||
|         if (!app) | ||||
|             throw new InvalidAppError(); | ||||
|  | ||||
|         super(app.get_name()); | ||||
|         super._init(app.get_name()); | ||||
|  | ||||
|         this._appId = appId; | ||||
|         this._app = app; | ||||
| @@ -690,7 +692,7 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source { | ||||
|         this._notifications[notificationId] = notification; | ||||
|  | ||||
|         if (showBanner) | ||||
|             this.notify(notification); | ||||
|             this.showNotification(notification); | ||||
|         else | ||||
|             this.pushNotification(notification); | ||||
|  | ||||
| @@ -716,7 +718,7 @@ class GtkNotificationDaemonAppSource extends MessageTray.Source { | ||||
|         } | ||||
|         return [this._appId, notifications]; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| const GtkNotificationsIface = loadInterfaceXML('org.gtk.Notifications'); | ||||
|  | ||||
| @@ -742,7 +744,7 @@ var GtkNotificationDaemon = class GtkNotificationDaemon { | ||||
|             delete this._sources[appId]; | ||||
|             this._saveNotifications(); | ||||
|         }); | ||||
|         source.connect('count-updated', this._saveNotifications.bind(this)); | ||||
|         source.connect('notify::count', this._saveNotifications.bind(this)); | ||||
|         Main.messageTray.add(source); | ||||
|         this._sources[appId] = source; | ||||
|         return source; | ||||
|   | ||||
| @@ -1,30 +1,33 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported OsdMonitorLabeler */ | ||||
|  | ||||
| const { Clutter, Gio, Meta, St } = imports.gi; | ||||
| const { Clutter, Gio, GObject, Meta, St } = imports.gi; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| var OsdMonitorLabel = class { | ||||
|     constructor(monitor, label) { | ||||
|         this._actor = new St.Widget({ x_expand: true, | ||||
|                                       y_expand: true }); | ||||
| var OsdMonitorLabel = GObject.registerClass( | ||||
| class OsdMonitorLabel extends St.Widget { | ||||
|     _init(monitor, label) { | ||||
|         super._init({ x_expand: true, y_expand: true }); | ||||
|  | ||||
|         this._monitor = monitor; | ||||
|  | ||||
|         this._box = new St.BoxLayout({ style_class: 'osd-window', | ||||
|                                        vertical: true }); | ||||
|         this._actor.add_actor(this._box); | ||||
|         this.add_actor(this._box); | ||||
|  | ||||
|         this._label = new St.Label({ style_class: 'osd-monitor-label', | ||||
|                                      text: label }); | ||||
|         this._box.add(this._label); | ||||
|  | ||||
|         Main.uiGroup.add_child(this._actor); | ||||
|         Main.uiGroup.set_child_above_sibling(this._actor, null); | ||||
|         Main.uiGroup.add_child(this); | ||||
|         Main.uiGroup.set_child_above_sibling(this, null); | ||||
|         this._position(); | ||||
|  | ||||
|         Meta.disable_unredirect_for_display(global.display); | ||||
|         this.connect('destroy', () => { | ||||
|             Meta.enable_unredirect_for_display(global.display); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _position() { | ||||
| @@ -37,12 +40,7 @@ var OsdMonitorLabel = class { | ||||
|  | ||||
|         this._box.y = workArea.y; | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|         this._actor.destroy(); | ||||
|         Meta.enable_unredirect_for_display(global.display); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var OsdMonitorLabeler = class { | ||||
|     constructor() { | ||||
| @@ -68,7 +66,7 @@ var OsdMonitorLabeler = class { | ||||
|  | ||||
|     _trackClient(client) { | ||||
|         if (this._client) | ||||
|             return (this._client == client); | ||||
|             return this._client == client; | ||||
|  | ||||
|         this._client = client; | ||||
|         this._clientWatchId = Gio.bus_watch_name(Gio.BusType.SESSION, client, 0, null, | ||||
|   | ||||
| @@ -41,39 +41,42 @@ class OsdWindowConstraint extends Clutter.Constraint { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var OsdWindow = class { | ||||
|     constructor(monitorIndex) { | ||||
|         this.actor = new St.Widget({ x_expand: true, | ||||
|                                      y_expand: true, | ||||
|                                      x_align: Clutter.ActorAlign.CENTER, | ||||
|                                      y_align: Clutter.ActorAlign.CENTER }); | ||||
| var OsdWindow = GObject.registerClass( | ||||
| class OsdWindow extends St.Widget { | ||||
|     _init(monitorIndex) { | ||||
|         super._init({ | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|  | ||||
|         this._monitorIndex = monitorIndex; | ||||
|         let constraint = new Layout.MonitorConstraint({ index: monitorIndex }); | ||||
|         this.actor.add_constraint(constraint); | ||||
|         this.add_constraint(constraint); | ||||
|  | ||||
|         this._boxConstraint = new OsdWindowConstraint(); | ||||
|         this._box = new St.BoxLayout({ style_class: 'osd-window', | ||||
|                                        vertical: true }); | ||||
|         this._box.add_constraint(this._boxConstraint); | ||||
|         this.actor.add_actor(this._box); | ||||
|         this.add_actor(this._box); | ||||
|  | ||||
|         this._icon = new St.Icon(); | ||||
|         this._box.add(this._icon, { expand: true }); | ||||
|         this._icon = new St.Icon({ y_expand: true }); | ||||
|         this._box.add_child(this._icon); | ||||
|  | ||||
|         this._label = new St.Label(); | ||||
|         this._box.add(this._label); | ||||
|  | ||||
|         this._level = new BarLevel.BarLevel({ | ||||
|             style_class: 'level', | ||||
|             value: 0 | ||||
|             value: 0, | ||||
|         }); | ||||
|         this._box.add(this._level); | ||||
|  | ||||
|         this._hideTimeoutId = 0; | ||||
|         this._reset(); | ||||
|  | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|  | ||||
|         this._monitorsChangedId = | ||||
|             Main.layoutManager.connect('monitors-changed', | ||||
| @@ -83,7 +86,7 @@ var OsdWindow = class { | ||||
|             themeContext.connect('notify::scale-factor', | ||||
|                                  this._relayout.bind(this)); | ||||
|         this._relayout(); | ||||
|         Main.uiGroup.add_child(this.actor); | ||||
|         Main.uiGroup.add_child(this); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
| @@ -102,21 +105,22 @@ var OsdWindow = class { | ||||
|     } | ||||
|  | ||||
|     setLabel(label) { | ||||
|         this._label.visible = (label != undefined); | ||||
|         this._label.visible = label != undefined; | ||||
|         if (label) | ||||
|             this._label.text = label; | ||||
|     } | ||||
|  | ||||
|     setLevel(value) { | ||||
|         this._level.visible = (value != undefined); | ||||
|         this._level.visible = value != undefined; | ||||
|         if (value != undefined) { | ||||
|             if (this.actor.visible) | ||||
|             if (this.visible) { | ||||
|                 this._level.ease_property('value', value, { | ||||
|                     mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                     duration: LEVEL_ANIMATION_TIME | ||||
|                     duration: LEVEL_ANIMATION_TIME, | ||||
|                 }); | ||||
|             else | ||||
|             } else { | ||||
|                 this._level.value = value; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -128,16 +132,16 @@ var OsdWindow = class { | ||||
|         if (!this._icon.gicon) | ||||
|             return; | ||||
|  | ||||
|         if (!this.actor.visible) { | ||||
|         if (!this.visible) { | ||||
|             Meta.disable_unredirect_for_display(global.display); | ||||
|             this.actor.show(); | ||||
|             this.actor.opacity = 0; | ||||
|             this.actor.get_parent().set_child_above_sibling(this.actor, null); | ||||
|             super.show(); | ||||
|             this.opacity = 0; | ||||
|             this.get_parent().set_child_above_sibling(this, null); | ||||
|  | ||||
|             this.actor.ease({ | ||||
|             this.ease({ | ||||
|                 opacity: 255, | ||||
|                 duration: FADE_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|         } | ||||
|  | ||||
| @@ -158,20 +162,20 @@ var OsdWindow = class { | ||||
|  | ||||
|     _hide() { | ||||
|         this._hideTimeoutId = 0; | ||||
|         this.actor.ease({ | ||||
|         this.ease({ | ||||
|             opacity: 0, | ||||
|             duration: FADE_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => { | ||||
|                 this._reset(); | ||||
|                 Meta.enable_unredirect_for_display(global.display); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     } | ||||
|  | ||||
|     _reset() { | ||||
|         this.actor.hide(); | ||||
|         super.hide(); | ||||
|         this.setLabel(null); | ||||
|         this.setMaxLevel(null); | ||||
|         this.setLevel(null); | ||||
| @@ -193,7 +197,7 @@ var OsdWindow = class { | ||||
|         this._box.translation_y = Math.round(monitor.height / 4); | ||||
|         this._boxConstraint.minSize = popupSize; | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var OsdWindowManager = class { | ||||
|     constructor() { | ||||
| @@ -210,7 +214,7 @@ var OsdWindowManager = class { | ||||
|         } | ||||
|  | ||||
|         for (let i = Main.layoutManager.monitors.length; i < this._osdWindows.length; i++) { | ||||
|             this._osdWindows[i].actor.destroy(); | ||||
|             this._osdWindows[i].destroy(); | ||||
|             this._osdWindows[i] = null; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported Overview */ | ||||
|  | ||||
| const { Clutter, GLib, Meta, Shell, St } = imports.gi; | ||||
| const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Background = imports.ui.background; | ||||
| @@ -72,36 +72,109 @@ var ShellInfo = class { | ||||
|         if (undoCallback) | ||||
|             notification.addAction(_("Undo"), this._onUndoClicked.bind(this)); | ||||
|  | ||||
|         this._source.notify(notification); | ||||
|         this._source.showNotification(notification); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var OverviewActor = GObject.registerClass( | ||||
| class OverviewActor extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             name: 'overview', | ||||
|             /* Translators: This is the main view to select | ||||
|                 activities. See also note for "Activities" string. */ | ||||
|             accessible_name: _("Overview"), | ||||
|             vertical: true, | ||||
|         }); | ||||
|  | ||||
|         this.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); | ||||
|  | ||||
|         // Add a clone of the panel to the overview so spacing and such is | ||||
|         // automatic | ||||
|         let panelGhost = new St.Bin({ | ||||
|             child: new Clutter.Clone({ source: Main.panel }), | ||||
|             reactive: false, | ||||
|             opacity: 0, | ||||
|         }); | ||||
|         this.add_actor(panelGhost); | ||||
|  | ||||
|         this._searchEntry = new St.Entry({ | ||||
|             style_class: 'search-entry', | ||||
|             /* Translators: this is the text displayed | ||||
|                in the search entry when no search is | ||||
|                active; it should not exceed ~30 | ||||
|                characters. */ | ||||
|             hint_text: _("Type to search…"), | ||||
|             track_hover: true, | ||||
|             can_focus: true, | ||||
|         }); | ||||
|         this._searchEntry.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); | ||||
|         let searchEntryBin = new St.Bin({ | ||||
|             child: this._searchEntry, | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         this.add_actor(searchEntryBin); | ||||
|  | ||||
|         this._controls = new OverviewControls.ControlsManager(this._searchEntry); | ||||
|  | ||||
|         // Add our same-line elements after the search entry | ||||
|         this.add_child(this._controls); | ||||
|     } | ||||
|  | ||||
|     get dash() { | ||||
|         return this._controls.dash; | ||||
|     } | ||||
|  | ||||
|     get searchEntry() { | ||||
|         return this._searchEntry; | ||||
|     } | ||||
|  | ||||
|     get viewSelector() { | ||||
|         return this._controls.viewSelector; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var Overview = class { | ||||
|     constructor() { | ||||
|         this._overviewCreated = false; | ||||
|         this._initCalled = false; | ||||
|  | ||||
|         Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); | ||||
|         this._sessionUpdated(); | ||||
|     } | ||||
|  | ||||
|     get dash() { | ||||
|         return this._overview.dash; | ||||
|     } | ||||
|  | ||||
|     get dashIconSize() { | ||||
|         logError(new Error('Usage of Overview.\'dashIconSize\' is deprecated, ' + | ||||
|             'use \'dash.iconSize\' property instead')); | ||||
|         return this.dash.iconSize; | ||||
|     } | ||||
|  | ||||
|     get viewSelector() { | ||||
|         return this._overview.viewSelector; | ||||
|     } | ||||
|  | ||||
|     get animationInProgress() { | ||||
|         return this._animationInProgress; | ||||
|     } | ||||
|  | ||||
|     get visible() { | ||||
|         return this._visible; | ||||
|     } | ||||
|  | ||||
|     get visibleTarget() { | ||||
|         return this._visibleTarget; | ||||
|     } | ||||
|  | ||||
|     _createOverview() { | ||||
|         if (this._overviewCreated) | ||||
|         if (this._overview) | ||||
|             return; | ||||
|  | ||||
|         if (this.isDummy) | ||||
|             return; | ||||
|  | ||||
|         this._overviewCreated = true; | ||||
|  | ||||
|         this._overview = new St.BoxLayout({ name: 'overview', | ||||
|                                             /* Translators: This is the main view to select | ||||
|                                                activities. See also note for "Activities" string. */ | ||||
|                                             accessible_name: _("Overview"), | ||||
|                                             vertical: true }); | ||||
|         this._overview.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); | ||||
|         this._overview._delegate = this; | ||||
|  | ||||
|         // The main Background actors are inside global.window_group which are | ||||
|         // hidden when displaying the overview, so we create a new | ||||
|         // one. Instances of this class share a single CoglTexture behind the | ||||
| @@ -116,11 +189,11 @@ var Overview = class { | ||||
|  | ||||
|         this._activationTime = 0; | ||||
|  | ||||
|         this.visible = false;           // animating to overview, in overview, animating out | ||||
|         this._visible = false;          // animating to overview, in overview, animating out | ||||
|         this._shown = false;            // show() and not hide() | ||||
|         this._modal = false;            // have a modal grab | ||||
|         this.animationInProgress = false; | ||||
|         this.visibleTarget = false; | ||||
|         this._animationInProgress = false; | ||||
|         this._visibleTarget = false; | ||||
|  | ||||
|         // During transitions, we raise this to the top to avoid having the overview | ||||
|         // area be reactive; it causes too many issues such as double clicks on | ||||
| @@ -129,14 +202,11 @@ var Overview = class { | ||||
|                                               reactive: true }); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._coverPane); | ||||
|         this._coverPane.connect('event', () => Clutter.EVENT_STOP); | ||||
|  | ||||
|         Main.layoutManager.overviewGroup.add_child(this._overview); | ||||
|  | ||||
|         this._coverPane.hide(); | ||||
|  | ||||
|         // XDND | ||||
|         this._dragMonitor = { | ||||
|             dragMotion: this._onDragMotion.bind(this) | ||||
|             dragMotion: this._onDragMotion.bind(this), | ||||
|         }; | ||||
|  | ||||
|  | ||||
| @@ -175,11 +245,11 @@ var Overview = class { | ||||
|         for (let i = 0; i < backgrounds.length; i++) { | ||||
|             backgrounds[i].ease_property('brightness', 1.0, { | ||||
|                 duration: SHADE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|             backgrounds[i].ease_property('vignette-sharpness', 0.0, { | ||||
|                 duration: SHADE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -189,11 +259,11 @@ var Overview = class { | ||||
|         for (let i = 0; i < backgrounds.length; i++) { | ||||
|             backgrounds[i].ease_property('brightness', Lightbox.VIGNETTE_BRIGHTNESS, { | ||||
|                 duration: SHADE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|             backgrounds[i].ease_property('vignette-sharpness', Lightbox.VIGNETTE_SHARPNESS, { | ||||
|                 duration: SHADE_ANIMATION_TIME, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -213,41 +283,12 @@ var Overview = class { | ||||
|         if (this.isDummy) | ||||
|             return; | ||||
|  | ||||
|         this._overview = new OverviewActor(); | ||||
|         this._overview._delegate = this; | ||||
|         Main.layoutManager.overviewGroup.add_child(this._overview); | ||||
|  | ||||
|         this._shellInfo = new ShellInfo(); | ||||
|  | ||||
|         // Add a clone of the panel to the overview so spacing and such is | ||||
|         // automatic | ||||
|         this._panelGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.panel }), | ||||
|                                         reactive: false, | ||||
|                                         opacity: 0 }); | ||||
|         this._overview.add_actor(this._panelGhost); | ||||
|  | ||||
|         this._searchEntry = new St.Entry({ style_class: 'search-entry', | ||||
|                                            /* Translators: this is the text displayed | ||||
|                                               in the search entry when no search is | ||||
|                                               active; it should not exceed ~30 | ||||
|                                               characters. */ | ||||
|                                            hint_text: _("Type to search…"), | ||||
|                                            track_hover: true, | ||||
|                                            can_focus: true }); | ||||
|         this._searchEntryBin = new St.Bin({ child: this._searchEntry, | ||||
|                                             x_align: St.Align.MIDDLE }); | ||||
|         this._overview.add_actor(this._searchEntryBin); | ||||
|  | ||||
|         // Create controls | ||||
|         this._controls = new OverviewControls.ControlsManager(this._searchEntry); | ||||
|         this._dash = this._controls.dash; | ||||
|         this.viewSelector = this._controls.viewSelector; | ||||
|  | ||||
|         // Add our same-line elements after the search entry | ||||
|         this._overview.add(this._controls.actor, { y_fill: true, expand: true }); | ||||
|  | ||||
|         // TODO - recalculate everything when desktop size changes | ||||
|         this.dashIconSize = this._dash.iconSize; | ||||
|         this._dash.connect('icon-size-changed', () => { | ||||
|             this.dashIconSize = this._dash.iconSize; | ||||
|         }); | ||||
|  | ||||
|         Main.layoutManager.connect('monitors-changed', this._relayout.bind(this)); | ||||
|         this._relayout(); | ||||
|     } | ||||
| @@ -426,7 +467,7 @@ var Overview = class { | ||||
|  | ||||
|     focusSearch() { | ||||
|         this.show(); | ||||
|         this._searchEntry.grab_key_focus(); | ||||
|         this._overview.searchEntry.grab_key_focus(); | ||||
|     } | ||||
|  | ||||
|     fadeInDesktop() { | ||||
| @@ -435,7 +476,7 @@ var Overview = class { | ||||
|         this._desktopFade.ease({ | ||||
|             opacity: 255, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             duration: ANIMATION_TIME | ||||
|             duration: ANIMATION_TIME, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -453,7 +494,7 @@ var Overview = class { | ||||
|         this._desktopFade.ease({ | ||||
|             opacity: 0, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             duration: ANIMATION_TIME | ||||
|             duration: ANIMATION_TIME, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -464,11 +505,11 @@ var Overview = class { | ||||
|     // the overview if the user both triggered the hot corner and | ||||
|     // clicked the Activities button. | ||||
|     shouldToggleByCornerOrButton() { | ||||
|         if (this.animationInProgress) | ||||
|         if (this._animationInProgress) | ||||
|             return false; | ||||
|         if (this._inItemDrag || this._inWindowDrag) | ||||
|             return false; | ||||
|         if (this._activationTime == 0 || | ||||
|         if (!this._activationTime || | ||||
|             GLib.get_monotonic_time() / GLib.USEC_PER_SEC - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT) | ||||
|             return true; | ||||
|         return false; | ||||
| @@ -478,7 +519,7 @@ var Overview = class { | ||||
|         // We delay grab changes during animation so that when removing the | ||||
|         // overview we don't have a problem with the release of a press/release | ||||
|         // going to an application. | ||||
|         if (this.animationInProgress) | ||||
|         if (this._animationInProgress) | ||||
|             return true; | ||||
|  | ||||
|         if (this._shown) { | ||||
| @@ -493,6 +534,7 @@ var Overview = class { | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             // eslint-disable-next-line no-lonely-if | ||||
|             if (this._modal) { | ||||
|                 Main.popModal(this._overview); | ||||
|                 this._modal = false; | ||||
| @@ -520,12 +562,12 @@ var Overview = class { | ||||
|  | ||||
|  | ||||
|     _animateVisible() { | ||||
|         if (this.visible || this.animationInProgress) | ||||
|         if (this._visible || this._animationInProgress) | ||||
|             return; | ||||
|  | ||||
|         this.visible = true; | ||||
|         this.animationInProgress = true; | ||||
|         this.visibleTarget = true; | ||||
|         this._visible = true; | ||||
|         this._animationInProgress = true; | ||||
|         this._visibleTarget = true; | ||||
|         this._activationTime = GLib.get_monotonic_time() / GLib.USEC_PER_SEC; | ||||
|  | ||||
|         Meta.disable_unredirect_for_display(global.display); | ||||
| @@ -536,17 +578,18 @@ var Overview = class { | ||||
|             opacity: 255, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             duration: ANIMATION_TIME, | ||||
|             onComplete: () => this._showDone() | ||||
|             onComplete: () => this._showDone(), | ||||
|         }); | ||||
|         this._shadeBackgrounds(); | ||||
|  | ||||
|         this._coverPane.raise_top(); | ||||
|         Main.layoutManager.overviewGroup.set_child_above_sibling( | ||||
|             this._coverPane, null); | ||||
|         this._coverPane.show(); | ||||
|         this.emit('showing'); | ||||
|     } | ||||
|  | ||||
|     _showDone() { | ||||
|         this.animationInProgress = false; | ||||
|         this._animationInProgress = false; | ||||
|         this._desktopFade.hide(); | ||||
|         this._coverPane.hide(); | ||||
|  | ||||
| @@ -572,8 +615,8 @@ var Overview = class { | ||||
|         let event = Clutter.get_current_event(); | ||||
|         if (event) { | ||||
|             let type = event.type(); | ||||
|             let button = (type == Clutter.EventType.BUTTON_PRESS || | ||||
|                           type == Clutter.EventType.BUTTON_RELEASE); | ||||
|             let button = type == Clutter.EventType.BUTTON_PRESS || | ||||
|                           type == Clutter.EventType.BUTTON_RELEASE; | ||||
|             let ctrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0; | ||||
|             if (button && ctrl) | ||||
|                 return; | ||||
| @@ -586,11 +629,11 @@ var Overview = class { | ||||
|     } | ||||
|  | ||||
|     _animateNotVisible() { | ||||
|         if (!this.visible || this.animationInProgress) | ||||
|         if (!this._visible || this._animationInProgress) | ||||
|             return; | ||||
|  | ||||
|         this.animationInProgress = true; | ||||
|         this.visibleTarget = false; | ||||
|         this._animationInProgress = true; | ||||
|         this._visibleTarget = false; | ||||
|  | ||||
|         this.viewSelector.animateFromOverview(); | ||||
|  | ||||
| @@ -599,11 +642,12 @@ var Overview = class { | ||||
|             opacity: 0, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             duration: ANIMATION_TIME, | ||||
|             onComplete: () => this._hideDone() | ||||
|             onComplete: () => this._hideDone(), | ||||
|         }); | ||||
|         this._unshadeBackgrounds(); | ||||
|  | ||||
|         this._coverPane.raise_top(); | ||||
|         Main.layoutManager.overviewGroup.set_child_above_sibling( | ||||
|             this._coverPane, null); | ||||
|         this._coverPane.show(); | ||||
|         this.emit('hiding'); | ||||
|     } | ||||
| @@ -616,8 +660,8 @@ var Overview = class { | ||||
|         this._desktopFade.hide(); | ||||
|         this._coverPane.hide(); | ||||
|  | ||||
|         this.visible = false; | ||||
|         this.animationInProgress = false; | ||||
|         this._visible = false; | ||||
|         this._animationInProgress = false; | ||||
|  | ||||
|         this.emit('hidden'); | ||||
|         // Handle any calls to show* while we were hiding | ||||
| @@ -633,14 +677,17 @@ var Overview = class { | ||||
|         if (this.isDummy) | ||||
|             return; | ||||
|  | ||||
|         if (this.visible) | ||||
|         if (this._visible) | ||||
|             this.hide(); | ||||
|         else | ||||
|             this.show(); | ||||
|     } | ||||
|  | ||||
|     getShowAppsButton() { | ||||
|         return this._dash.showAppsButton; | ||||
|         logError(new Error('Usage of Overview.\'getShowAppsButton\' is deprecated, ' + | ||||
|             'use \'dash.showAppsButton\' property instead')); | ||||
|  | ||||
|         return this.dash.showAppsButton; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(Overview.prototype); | ||||
|   | ||||
| @@ -12,17 +12,17 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail; | ||||
| var SIDE_CONTROLS_ANIMATION_TIME = 160; | ||||
|  | ||||
| function getRtlSlideDirection(direction, actor) { | ||||
|     let rtl = (actor.text_direction == Clutter.TextDirection.RTL); | ||||
|     if (rtl) | ||||
|         direction = (direction == SlideDirection.LEFT) | ||||
|     let rtl = actor.text_direction == Clutter.TextDirection.RTL; | ||||
|     if (rtl) { | ||||
|         direction = direction == SlideDirection.LEFT | ||||
|             ? SlideDirection.RIGHT : SlideDirection.LEFT; | ||||
|  | ||||
|     } | ||||
|     return direction; | ||||
| } | ||||
|  | ||||
| var SlideDirection = { | ||||
|     LEFT: 0, | ||||
|     RIGHT: 1 | ||||
|     RIGHT: 1, | ||||
| }; | ||||
|  | ||||
| var SlideLayout = GObject.registerClass({ | ||||
| @@ -34,8 +34,8 @@ var SlideLayout = GObject.registerClass({ | ||||
|         'translation-x': GObject.ParamSpec.double( | ||||
|             'translation-x', 'translation-x', 'translation-x', | ||||
|             GObject.ParamFlags.READWRITE, | ||||
|             -Infinity, Infinity, 0) | ||||
|     } | ||||
|             -Infinity, Infinity, 0), | ||||
|     }, | ||||
| }, class SlideLayout extends Clutter.FixedLayout { | ||||
|     _init(params) { | ||||
|         this._slideX = 1; | ||||
| @@ -67,7 +67,7 @@ var SlideLayout = GObject.registerClass({ | ||||
|         // flags only determine what to do if the allocated box is bigger | ||||
|         // than the actor's box. | ||||
|         let realDirection = getRtlSlideDirection(this._direction, child); | ||||
|         let alignX = (realDirection == SlideDirection.LEFT) | ||||
|         let alignX = realDirection == SlideDirection.LEFT | ||||
|             ? availWidth - natWidth | ||||
|             : availWidth - natWidth * this._slideX; | ||||
|  | ||||
| @@ -118,18 +118,21 @@ var SlideLayout = GObject.registerClass({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var SlidingControl = class { | ||||
|     constructor(params) { | ||||
| var SlidingControl = GObject.registerClass( | ||||
| class SlidingControl extends St.Widget { | ||||
|     _init(params) { | ||||
|         params = Params.parse(params, { slideDirection: SlideDirection.LEFT }); | ||||
|  | ||||
|         this._visible = true; | ||||
|         this._inDrag = false; | ||||
|  | ||||
|         this.layout = new SlideLayout(); | ||||
|         this.layout.slideDirection = params.slideDirection; | ||||
|         this.actor = new St.Widget({ layout_manager: this.layout, | ||||
|                                      style_class: 'overview-controls', | ||||
|                                      clip_to_allocation: true }); | ||||
|         super._init({ | ||||
|             layout_manager: this.layout, | ||||
|             style_class: 'overview-controls', | ||||
|             clip_to_allocation: true, | ||||
|         }); | ||||
|  | ||||
|         this._visible = true; | ||||
|         this._inDrag = false; | ||||
|  | ||||
|         Main.overview.connect('hiding', this._onOverviewHiding.bind(this)); | ||||
|  | ||||
| @@ -147,25 +150,25 @@ var SlidingControl = class { | ||||
|     } | ||||
|  | ||||
|     _updateSlide() { | ||||
|         this.actor.ease_property('@layout.slide-x', this._getSlide(), { | ||||
|         this.ease_property('@layout.slide-x', this._getSlide(), { | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             duration: SIDE_CONTROLS_ANIMATION_TIME, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     getVisibleWidth() { | ||||
|         let child = this.actor.get_first_child(); | ||||
|         let child = this.get_first_child(); | ||||
|         let [, , natWidth] = child.get_preferred_size(); | ||||
|         return natWidth; | ||||
|     } | ||||
|  | ||||
|     _getTranslation() { | ||||
|         let child = this.actor.get_first_child(); | ||||
|         let child = this.get_first_child(); | ||||
|         let direction = getRtlSlideDirection(this.layout.slideDirection, child); | ||||
|         let visibleWidth = this.getVisibleWidth(); | ||||
|  | ||||
|         if (direction == SlideDirection.LEFT) | ||||
|             return - visibleWidth; | ||||
|             return -visibleWidth; | ||||
|         else | ||||
|             return visibleWidth; | ||||
|     } | ||||
| @@ -175,18 +178,17 @@ var SlidingControl = class { | ||||
|         let translationEnd = 0; | ||||
|         let translation = this._getTranslation(); | ||||
|  | ||||
|         let shouldShow = (this._getSlide() > 0); | ||||
|         if (shouldShow) { | ||||
|         let shouldShow = this._getSlide() > 0; | ||||
|         if (shouldShow) | ||||
|             translationStart = translation; | ||||
|         } else { | ||||
|         else | ||||
|             translationEnd = translation; | ||||
|         } | ||||
|  | ||||
|         if (this.layout.translation_x == translationEnd) | ||||
|             return; | ||||
|  | ||||
|         this.layout.translation_x = translationStart; | ||||
|         this.actor.ease_property('@layout.translation-x', translationEnd, { | ||||
|         this.ease_property('@layout.translation-x', translationEnd, { | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             duration: SIDE_CONTROLS_ANIMATION_TIME, | ||||
|         }); | ||||
| @@ -218,18 +220,18 @@ var SlidingControl = class { | ||||
|     } | ||||
|  | ||||
|     fadeIn() { | ||||
|         this.actor.ease({ | ||||
|         this.ease({ | ||||
|             opacity: 255, | ||||
|             duration: SIDE_CONTROLS_ANIMATION_TIME / 2, | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     fadeHalf() { | ||||
|         this.actor.ease({ | ||||
|         this.ease({ | ||||
|             opacity: 128, | ||||
|             duration: SIDE_CONTROLS_ANIMATION_TIME / 2, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -249,37 +251,38 @@ var SlidingControl = class { | ||||
|         // selector; this means we can now safely set the full slide for | ||||
|         // the next page, since slideIn or slideOut might have been called, | ||||
|         // changing the visiblity | ||||
|         this.actor.remove_transition('@layout.slide-x'); | ||||
|         this.remove_transition('@layout.slide-x'); | ||||
|         this.layout.slide_x = this._getSlide(); | ||||
|         this._updateTranslation(); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var ThumbnailsSlider = class extends SlidingControl { | ||||
|     constructor(thumbnailsBox) { | ||||
|         super({ slideDirection: SlideDirection.RIGHT }); | ||||
| var ThumbnailsSlider = GObject.registerClass( | ||||
| class ThumbnailsSlider extends SlidingControl { | ||||
|     _init(thumbnailsBox) { | ||||
|         super._init({ slideDirection: SlideDirection.RIGHT }); | ||||
|  | ||||
|         this._thumbnailsBox = thumbnailsBox; | ||||
|  | ||||
|         this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT; | ||||
|         this.actor.reactive = true; | ||||
|         this.actor.track_hover = true; | ||||
|         this.actor.add_actor(this._thumbnailsBox); | ||||
|         this.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT; | ||||
|         this.reactive = true; | ||||
|         this.track_hover = true; | ||||
|         this.add_actor(this._thumbnailsBox); | ||||
|  | ||||
|         Main.layoutManager.connect('monitors-changed', this._updateSlide.bind(this)); | ||||
|         global.workspace_manager.connect('active-workspace-changed', | ||||
|                                          this._updateSlide.bind(this)); | ||||
|         global.workspace_manager.connect('notify::n-workspaces', | ||||
|                                          this._updateSlide.bind(this)); | ||||
|         this.actor.connect('notify::hover', this._updateSlide.bind(this)); | ||||
|         this._thumbnailsBox.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE); | ||||
|         this.connect('notify::hover', this._updateSlide.bind(this)); | ||||
|         this._thumbnailsBox.bind_property('visible', this, 'visible', GObject.BindingFlags.SYNC_CREATE); | ||||
|     } | ||||
|  | ||||
|     _getAlwaysZoomOut() { | ||||
|         // Always show the pager on hover, during a drag, or if workspaces are | ||||
|         // actually used, e.g. there are windows on any non-active workspace | ||||
|         let workspaceManager = global.workspace_manager; | ||||
|         let alwaysZoomOut = this.actor.hover || | ||||
|         let alwaysZoomOut = this.hover || | ||||
|                             this._inDrag || | ||||
|                             !Meta.prefs_get_dynamic_workspaces() || | ||||
|                             workspaceManager.n_workspaces > 2 || | ||||
| @@ -304,12 +307,12 @@ var ThumbnailsSlider = class extends SlidingControl { | ||||
|     } | ||||
|  | ||||
|     getNonExpandedWidth() { | ||||
|         let child = this.actor.get_first_child(); | ||||
|         let child = this.get_first_child(); | ||||
|         return child.get_theme_node().get_length('visible-width'); | ||||
|     } | ||||
|  | ||||
|     _onDragEnd() { | ||||
|         this.actor.sync_hover(); | ||||
|         this.sync_hover(); | ||||
|         super._onDragEnd(); | ||||
|     } | ||||
|  | ||||
| @@ -321,7 +324,7 @@ var ThumbnailsSlider = class extends SlidingControl { | ||||
|         if (alwaysZoomOut) | ||||
|             return 1; | ||||
|  | ||||
|         let child = this.actor.get_first_child(); | ||||
|         let child = this.get_first_child(); | ||||
|         let preferredHeight = child.get_preferred_height(-1)[1]; | ||||
|         let expandedWidth = child.get_preferred_width(preferredHeight)[1]; | ||||
|  | ||||
| @@ -335,24 +338,25 @@ var ThumbnailsSlider = class extends SlidingControl { | ||||
|         else | ||||
|             return this.getNonExpandedWidth(); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var DashSlider = class extends SlidingControl { | ||||
|     constructor(dash) { | ||||
|         super({ slideDirection: SlideDirection.LEFT }); | ||||
| var DashSlider = GObject.registerClass( | ||||
| class DashSlider extends SlidingControl { | ||||
|     _init(dash) { | ||||
|         super._init({ slideDirection: SlideDirection.LEFT }); | ||||
|  | ||||
|         this._dash = dash; | ||||
|  | ||||
|         // SlideLayout reads the actor's expand flags to decide | ||||
|         // whether to allocate the natural size to its child, or the whole | ||||
|         // available allocation | ||||
|         this._dash.actor.x_expand = true; | ||||
|         this._dash.x_expand = true; | ||||
|  | ||||
|         this.actor.x_expand = true; | ||||
|         this.actor.x_align = Clutter.ActorAlign.START; | ||||
|         this.actor.y_expand = true; | ||||
|         this.x_expand = true; | ||||
|         this.x_align = Clutter.ActorAlign.START; | ||||
|         this.y_expand = true; | ||||
|  | ||||
|         this.actor.add_actor(this._dash.actor); | ||||
|         this.add_actor(this._dash); | ||||
|  | ||||
|         this._dash.connect('icon-size-changed', this._updateSlide.bind(this)); | ||||
|     } | ||||
| @@ -371,7 +375,7 @@ var DashSlider = class extends SlidingControl { | ||||
|     _onWindowDragEnd() { | ||||
|         this.fadeIn(); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var DashSpacer = GObject.registerClass( | ||||
| class DashSpacer extends St.Widget { | ||||
| @@ -416,12 +420,21 @@ var ControlsLayout = GObject.registerClass({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var ControlsManager = class { | ||||
|     constructor(searchEntry) { | ||||
| var ControlsManager = GObject.registerClass( | ||||
| class ControlsManager extends St.Widget { | ||||
|     _init(searchEntry) { | ||||
|         let layout = new ControlsLayout(); | ||||
|         super._init({ | ||||
|             layout_manager: layout, | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             clip_to_allocation: true, | ||||
|         }); | ||||
|  | ||||
|         this.dash = new Dash.Dash(); | ||||
|         this._dashSlider = new DashSlider(this.dash); | ||||
|         this._dashSpacer = new DashSpacer(); | ||||
|         this._dashSpacer.setDashActor(this._dashSlider.actor); | ||||
|         this._dashSpacer.setDashActor(this._dashSlider); | ||||
|  | ||||
|         this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox(); | ||||
|         this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox); | ||||
| @@ -431,20 +444,15 @@ var ControlsManager = class { | ||||
|         this.viewSelector.connect('page-changed', this._setVisibility.bind(this)); | ||||
|         this.viewSelector.connect('page-empty', this._onPageEmpty.bind(this)); | ||||
|  | ||||
|         let layout = new ControlsLayout(); | ||||
|         this.actor = new St.Widget({ layout_manager: layout, | ||||
|                                      x_expand: true, y_expand: true, | ||||
|                                      clip_to_allocation: true }); | ||||
|         this._group = new St.BoxLayout({ name: 'overview-group', | ||||
|                                          x_expand: true, y_expand: true }); | ||||
|         this.actor.add_actor(this._group); | ||||
|         this.add_actor(this._group); | ||||
|  | ||||
|         this.actor.add_actor(this._dashSlider.actor); | ||||
|         this.add_actor(this._dashSlider); | ||||
|  | ||||
|         this._group.add_actor(this._dashSpacer); | ||||
|         this._group.add(this.viewSelector.actor, { x_fill: true, | ||||
|                                                    expand: true }); | ||||
|         this._group.add_actor(this._thumbnailsSlider.actor); | ||||
|         this._group.add_child(this.viewSelector); | ||||
|         this._group.add_actor(this._thumbnailsSlider); | ||||
|  | ||||
|         layout.connect('allocation-changed', this._updateWorkspacesGeometry.bind(this)); | ||||
|  | ||||
| @@ -452,18 +460,18 @@ var ControlsManager = class { | ||||
|     } | ||||
|  | ||||
|     _updateWorkspacesGeometry() { | ||||
|         let [x, y] = this.actor.get_transformed_position(); | ||||
|         let [width, height] = this.actor.get_transformed_size(); | ||||
|         let geometry = { x: x, y: y, width: width, height: height }; | ||||
|         let [x, y] = this.get_transformed_position(); | ||||
|         let [width, height] = this.get_transformed_size(); | ||||
|         let geometry = { x, y, width, height }; | ||||
|  | ||||
|         let spacing = this.actor.get_theme_node().get_length('spacing'); | ||||
|         let spacing = this.get_theme_node().get_length('spacing'); | ||||
|         let dashWidth = this._dashSlider.getVisibleWidth() + spacing; | ||||
|         let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing; | ||||
|  | ||||
|         geometry.width -= dashWidth; | ||||
|         geometry.width -= thumbnailsWidth; | ||||
|  | ||||
|         if (this.actor.get_text_direction() == Clutter.TextDirection.LTR) | ||||
|         if (this.get_text_direction() == Clutter.TextDirection.LTR) | ||||
|             geometry.x += dashWidth; | ||||
|         else | ||||
|             geometry.x += thumbnailsWidth; | ||||
| @@ -481,9 +489,9 @@ var ControlsManager = class { | ||||
|             return; | ||||
|  | ||||
|         let activePage = this.viewSelector.getActivePage(); | ||||
|         let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS || | ||||
|                            activePage == ViewSelector.ViewPage.APPS); | ||||
|         let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS); | ||||
|         let dashVisible = activePage == ViewSelector.ViewPage.WINDOWS || | ||||
|                            activePage == ViewSelector.ViewPage.APPS; | ||||
|         let thumbnailsVisible = activePage == ViewSelector.ViewPage.WINDOWS; | ||||
|  | ||||
|         if (dashVisible) | ||||
|             this._dashSlider.slideIn(); | ||||
| @@ -501,7 +509,7 @@ var ControlsManager = class { | ||||
|             return; | ||||
|  | ||||
|         let activePage = this.viewSelector.getActivePage(); | ||||
|         this._dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS); | ||||
|         this._dashSpacer.visible = activePage == ViewSelector.ViewPage.WINDOWS; | ||||
|     } | ||||
|  | ||||
|     _onPageEmpty() { | ||||
| @@ -510,4 +518,4 @@ var ControlsManager = class { | ||||
|  | ||||
|         this._updateSpacerVisibility(); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|   | ||||
							
								
								
									
										227
									
								
								js/ui/padOsd.js
									
									
									
									
									
								
							
							
						
						
									
										227
									
								
								js/ui/padOsd.js
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported PadOsdService */ | ||||
| /* exported PadOsd, PadOsdService */ | ||||
|  | ||||
| const { Atk, Clutter, GDesktopEnums, Gio, | ||||
|         GLib, GObject, Gtk, Meta, Rsvg, St } = imports.gi; | ||||
| @@ -22,40 +22,45 @@ const CCW = 1; | ||||
| const UP = 0; | ||||
| const DOWN = 1; | ||||
|  | ||||
| var PadChooser = class { | ||||
|     constructor(device, groupDevices) { | ||||
|         this.actor = new St.Button({ style_class: 'pad-chooser-button', | ||||
|                                      toggle_mode: true, | ||||
|                                      x_fill: false, | ||||
|                                      y_fill: false, | ||||
|                                      x_align: St.Align.MIDDLE, | ||||
|                                      y_align: St.Align.MIDDLE }); | ||||
| var PadChooser = GObject.registerClass({ | ||||
|     Signals: { 'pad-selected': { param_types: [Clutter.InputDevice.$gtype] } }, | ||||
| }, class PadChooser extends St.Button { | ||||
|     _init(device, groupDevices) { | ||||
|         super._init({ | ||||
|             style_class: 'pad-chooser-button', | ||||
|             toggle_mode: true, | ||||
|         }); | ||||
|         this.currentDevice = device; | ||||
|         this._padChooserMenu = null; | ||||
|  | ||||
|         let arrow = new St.Icon({ style_class: 'popup-menu-arrow', | ||||
|                                   icon_name: 'pan-down-symbolic', | ||||
|                                   accessible_role: Atk.Role.ARROW }); | ||||
|         this.actor.set_child(arrow); | ||||
|         let arrow = new St.Icon({ | ||||
|             style_class: 'popup-menu-arrow', | ||||
|             icon_name: 'pan-down-symbolic', | ||||
|             accessible_role: Atk.Role.ARROW, | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         this.set_child(arrow); | ||||
|         this._ensureMenu(groupDevices); | ||||
|  | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|         this.actor.connect('clicked', actor => { | ||||
|             if (actor.get_checked()) { | ||||
|                 if (this._padChooserMenu != null) | ||||
|                     this._padChooserMenu.open(true); | ||||
|                 else | ||||
|                     this.set_checked(false); | ||||
|             } else { | ||||
|                 this._padChooserMenu.close(true); | ||||
|             } | ||||
|         }); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|     vfunc_clicked() { | ||||
|         if (this.get_checked()) { | ||||
|             if (this._padChooserMenu != null) | ||||
|                 this._padChooserMenu.open(true); | ||||
|             else | ||||
|                 this.set_checked(false); | ||||
|         } else { | ||||
|             this._padChooserMenu.close(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _ensureMenu(devices) { | ||||
|         this._padChooserMenu =  new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.TOP); | ||||
|         this._padChooserMenu =  new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP); | ||||
|         this._padChooserMenu.connect('menu-closed', () => { | ||||
|             this.actor.set_checked(false); | ||||
|             this.set_checked(false); | ||||
|         }); | ||||
|         this._padChooserMenu.actor.hide(); | ||||
|         Main.uiGroup.add_actor(this._padChooserMenu.actor); | ||||
| @@ -78,24 +83,19 @@ var PadChooser = class { | ||||
|     update(devices) { | ||||
|         if (this._padChooserMenu) | ||||
|             this._padChooserMenu.actor.destroy(); | ||||
|         this.actor.set_checked(false); | ||||
|         this.set_checked(false); | ||||
|         this._ensureMenu(devices); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|     destroy() { | ||||
|         this.actor.destroy(); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(PadChooser.prototype); | ||||
|  | ||||
| var KeybindingEntry = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.Entry({ hint_text: _("New shortcut…"), | ||||
|                                     style: 'width: 10em' }); | ||||
|         this.actor.connect('captured-event', this._onCapturedEvent.bind(this)); | ||||
| var KeybindingEntry = GObject.registerClass({ | ||||
|     Signals: { 'keybinding-edited': {} }, | ||||
| }, class KeybindingEntry extends St.Entry { | ||||
|     _init() { | ||||
|         super._init({ hint_text: _("New shortcut…"), style: 'width: 10em' }); | ||||
|     } | ||||
|  | ||||
|     _onCapturedEvent(actor, event) { | ||||
|     vfunc_captured_event(event) { | ||||
|         if (event.type() != Clutter.EventType.KEY_PRESS) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
| @@ -103,23 +103,23 @@ var KeybindingEntry = class { | ||||
|                                                     event.get_key_symbol(), | ||||
|                                                     event.get_key_code(), | ||||
|                                                     event.get_state()); | ||||
|         this.actor.set_text(str); | ||||
|         this.set_text(str); | ||||
|         this.emit('keybinding-edited', str); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(KeybindingEntry.prototype); | ||||
| }); | ||||
|  | ||||
| var ActionComboBox = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.Button({ style_class: 'button' }); | ||||
|         this.actor.connect('clicked', this._onButtonClicked.bind(this)); | ||||
|         this.actor.set_toggle_mode(true); | ||||
| var ActionComboBox = GObject.registerClass({ | ||||
|     Signals: { 'action-selected': { param_types: [GObject.TYPE_INT] } }, | ||||
| }, class ActionComboBox extends St.Button { | ||||
|     _init() { | ||||
|         super._init({ style_class: 'button' }); | ||||
|         this.set_toggle_mode(true); | ||||
|  | ||||
|         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL, | ||||
|                                                 spacing: 6 }); | ||||
|         let box = new St.Widget({ layout_manager: boxLayout }); | ||||
|         this.actor.set_child(box); | ||||
|         this.set_child(box); | ||||
|  | ||||
|         this._label = new St.Label({ style_class: 'combo-box-label' }); | ||||
|         box.add_child(this._label); | ||||
| @@ -131,9 +131,9 @@ var ActionComboBox = class { | ||||
|                                   y_align: Clutter.ActorAlign.CENTER }); | ||||
|         box.add_child(arrow); | ||||
|  | ||||
|         this._editMenu = new PopupMenu.PopupMenu(this.actor, 0, St.Side.TOP); | ||||
|         this._editMenu = new PopupMenu.PopupMenu(this, 0, St.Side.TOP); | ||||
|         this._editMenu.connect('menu-closed', () => { | ||||
|             this.actor.set_checked(false); | ||||
|             this.set_checked(false); | ||||
|         }); | ||||
|         this._editMenu.actor.hide(); | ||||
|         Main.uiGroup.add_actor(this._editMenu.actor); | ||||
| @@ -179,8 +179,8 @@ var ActionComboBox = class { | ||||
|         this._editMenu.close(true); | ||||
|     } | ||||
|  | ||||
|     _onButtonClicked() { | ||||
|         if (this.actor.get_checked()) | ||||
|     vfunc_clicked() { | ||||
|         if (this.get_checked()) | ||||
|             this.popup(); | ||||
|         else | ||||
|             this.popdown(); | ||||
| @@ -189,38 +189,39 @@ var ActionComboBox = class { | ||||
|     setButtonActionsActive(active) { | ||||
|         this._buttonItems.forEach(item => item.setSensitive(active)); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(ActionComboBox.prototype); | ||||
| }); | ||||
|  | ||||
| var ActionEditor = class { | ||||
|     constructor() { | ||||
| var ActionEditor = GObject.registerClass({ | ||||
|     Signals: { 'done': {} }, | ||||
| }, class ActionEditor extends St.Widget { | ||||
|     _init() { | ||||
|         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL, | ||||
|                                                 spacing: 12 }); | ||||
|  | ||||
|         this.actor = new St.Widget({ layout_manager: boxLayout }); | ||||
|         super._init({ layout_manager: boxLayout }); | ||||
|  | ||||
|         this._actionComboBox = new ActionComboBox(); | ||||
|         this._actionComboBox.connect('action-selected', this._onActionSelected.bind(this)); | ||||
|         this.actor.add_actor(this._actionComboBox.actor); | ||||
|         this.add_actor(this._actionComboBox); | ||||
|  | ||||
|         this._keybindingEdit = new KeybindingEntry(); | ||||
|         this._keybindingEdit.connect('keybinding-edited', this._onKeybindingEdited.bind(this)); | ||||
|         this.actor.add_actor(this._keybindingEdit.actor); | ||||
|         this.add_actor(this._keybindingEdit); | ||||
|  | ||||
|         this._doneButton = new St.Button({ label: _("Done"), | ||||
|                                            style_class: 'button', | ||||
|                                            x_expand: false }); | ||||
|         this._doneButton.connect('clicked', this._onEditingDone.bind(this)); | ||||
|         this.actor.add_actor(this._doneButton); | ||||
|         this.add_actor(this._doneButton); | ||||
|     } | ||||
|  | ||||
|     _updateKeybindingEntryState() { | ||||
|         if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) { | ||||
|             this._keybindingEdit.actor.set_text(this._currentKeybinding); | ||||
|             this._keybindingEdit.actor.show(); | ||||
|             this._keybindingEdit.actor.grab_key_focus(); | ||||
|             this._keybindingEdit.set_text(this._currentKeybinding); | ||||
|             this._keybindingEdit.show(); | ||||
|             this._keybindingEdit.grab_key_focus(); | ||||
|         } else { | ||||
|             this._keybindingEdit.actor.hide(); | ||||
|             this._keybindingEdit.hide(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -232,13 +233,13 @@ var ActionEditor = class { | ||||
|         this._actionComboBox.setAction(this._currentAction); | ||||
|         this._updateKeybindingEntryState(); | ||||
|  | ||||
|         let isButton = (action == Meta.PadActionType.BUTTON); | ||||
|         let isButton = action == Meta.PadActionType.BUTTON; | ||||
|         this._actionComboBox.setButtonActionsActive(isButton); | ||||
|     } | ||||
|  | ||||
|     close() { | ||||
|         this._actionComboBox.popdown(); | ||||
|         this.actor.hide(); | ||||
|         this.hide(); | ||||
|     } | ||||
|  | ||||
|     _onKeybindingEdited(entry, keybinding) { | ||||
| @@ -272,8 +273,7 @@ var ActionEditor = class { | ||||
|         this.close(); | ||||
|         this.emit('done'); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(ActionEditor.prototype); | ||||
| }); | ||||
|  | ||||
| var PadDiagram = GObject.registerClass({ | ||||
|     Properties: { | ||||
| @@ -291,7 +291,7 @@ var PadDiagram = GObject.registerClass({ | ||||
|                                                  'Editor actor', | ||||
|                                                  GObject.ParamFlags.READWRITE | | ||||
|                                                  GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|                                                  Clutter.Actor.$gtype) | ||||
|                                                  Clutter.Actor.$gtype), | ||||
|     }, | ||||
| }, class PadDiagram extends St.DrawingArea { | ||||
|     _init(params) { | ||||
| @@ -344,17 +344,19 @@ var PadDiagram = GObject.registerClass({ | ||||
|     } | ||||
|  | ||||
|     _wrappingSvgHeader() { | ||||
|         return ('<?xml version="1.0" encoding="UTF-8" standalone="no"?>' + | ||||
|                 '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' + | ||||
|                 'xmlns:xi="http://www.w3.org/2001/XInclude" ' + | ||||
|                 `width="${this._imageWidth}" height="${this._imageHeight}"> ` + | ||||
|                 '<style type="text/css">'); | ||||
|         return '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' + | ||||
|                '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' + | ||||
|                'xmlns:xi="http://www.w3.org/2001/XInclude" ' + | ||||
|                `width="${ // " (give xgettext the paired quotes it expects) | ||||
|                    this._imageWidth | ||||
|                }" height="${this._imageHeight}"> ` + // " | ||||
|                '<style type="text/css">'; | ||||
|     } | ||||
|  | ||||
|     _wrappingSvgFooter() { | ||||
|         return ('</style>' + | ||||
|         return '</style>' + | ||||
|                 '<xi:include href="' + this._imagePath + '" />' + | ||||
|                 '</svg>'); | ||||
|                 '</svg>'; | ||||
|     } | ||||
|  | ||||
|     _cssString() { | ||||
| @@ -615,8 +617,21 @@ var PadDiagram = GObject.registerClass({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var PadOsd = class { | ||||
|     constructor(padDevice, settings, imagePath, editionMode, monitorIndex) { | ||||
| var PadOsd = GObject.registerClass({ | ||||
|     Signals: { | ||||
|         'pad-selected': { param_types: [Clutter.InputDevice.$gtype] }, | ||||
|         'closed': {}, | ||||
|     }, | ||||
| }, class PadOsd extends St.BoxLayout { | ||||
|     _init(padDevice, settings, imagePath, editionMode, monitorIndex) { | ||||
|         super._init({ | ||||
|             style_class: 'pad-osd-window', | ||||
|             vertical: true, | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|             reactive: true, | ||||
|         }); | ||||
|  | ||||
|         this.padDevice = padDevice; | ||||
|         this._groupPads = [padDevice]; | ||||
|         this._settings = settings; | ||||
| @@ -653,23 +668,18 @@ var PadOsd = class { | ||||
|                 this._groupPads.push(device); | ||||
|         }); | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ style_class: 'pad-osd-window', | ||||
|                                         x_expand: true, | ||||
|                                         y_expand: true, | ||||
|                                         vertical: true, | ||||
|                                         reactive: true }); | ||||
|         this.actor.connect('destroy', this._onDestroy.bind(this)); | ||||
|         Main.uiGroup.add_actor(this.actor); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|         Main.uiGroup.add_actor(this); | ||||
|  | ||||
|         this._monitorIndex = monitorIndex; | ||||
|         let constraint = new Layout.MonitorConstraint({ index: monitorIndex }); | ||||
|         this.actor.add_constraint(constraint); | ||||
|         this.add_constraint(constraint); | ||||
|  | ||||
|         this._titleBox = new St.BoxLayout({ style_class: 'pad-osd-title-box', | ||||
|                                             vertical: false, | ||||
|                                             x_expand: false, | ||||
|                                             x_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_actor(this._titleBox); | ||||
|         this.add_actor(this._titleBox); | ||||
|  | ||||
|         let labelBox = new St.BoxLayout({ style_class: 'pad-osd-title-menu-box', | ||||
|                                           vertical: true }); | ||||
| @@ -690,10 +700,10 @@ var PadOsd = class { | ||||
|  | ||||
|         this._padDiagram = new PadDiagram({ image: this._imagePath, | ||||
|                                             left_handed: settings.get_boolean('left-handed'), | ||||
|                                             editor_actor: this._actionEditor.actor, | ||||
|                                             editor_actor: this._actionEditor, | ||||
|                                             x_expand: true, | ||||
|                                             y_expand: true }); | ||||
|         this.actor.add_actor(this._padDiagram); | ||||
|         this.add_actor(this._padDiagram); | ||||
|  | ||||
|         // FIXME: Fix num buttons. | ||||
|         let i = 0; | ||||
| @@ -724,18 +734,20 @@ var PadOsd = class { | ||||
|                                         x_expand: true, | ||||
|                                         x_align: Clutter.ActorAlign.CENTER, | ||||
|                                         y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_actor(buttonBox); | ||||
|         this._editButton = new St.Button({ label: _("Edit…"), | ||||
|                                            style_class: 'button', | ||||
|                                            x_align: Clutter.ActorAlign.CENTER, | ||||
|                                            can_focus: true }); | ||||
|         this.add_actor(buttonBox); | ||||
|         this._editButton = new St.Button({ | ||||
|             label: _('Edit…'), | ||||
|             style_class: 'button', | ||||
|             can_focus: true, | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         this._editButton.connect('clicked', () => { | ||||
|             this.setEditionMode(true); | ||||
|         }); | ||||
|         buttonBox.add_actor(this._editButton); | ||||
|  | ||||
|         this._syncEditionMode(); | ||||
|         Main.pushModal(this.actor); | ||||
|         Main.pushModal(this); | ||||
|     } | ||||
|  | ||||
|     _updatePadChooser() { | ||||
| @@ -745,7 +757,7 @@ var PadOsd = class { | ||||
|                 this._padChooser.connect('pad-selected', (chooser, pad) => { | ||||
|                     this._requestForOtherPad(pad); | ||||
|                 }); | ||||
|                 this._titleBox.add_child(this._padChooser.actor); | ||||
|                 this._titleBox.add_child(this._padChooser); | ||||
|             } else { | ||||
|                 this._padChooser.update(this._groupPads); | ||||
|             } | ||||
| @@ -785,7 +797,7 @@ var PadOsd = class { | ||||
|             this._padDiagram.deactivateButton(event.get_button()); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (event.type() == Clutter.EventType.KEY_PRESS && | ||||
|                    (!this._editionMode || event.get_key_symbol() == Clutter.Escape)) { | ||||
|                    (!this._editionMode || event.get_key_symbol() === Clutter.KEY_Escape)) { | ||||
|             if (this._editedAction != null) | ||||
|                 this._endActionEdition(); | ||||
|             else | ||||
| @@ -832,22 +844,23 @@ var PadOsd = class { | ||||
|             this._tipLabel.set_text(_("Press any key to exit")); | ||||
|         } | ||||
|  | ||||
|         this._titleLabel.clutter_text.set_markup('<span size="larger"><b>' + title + '</b></span>'); | ||||
|         this._titleLabel.clutter_text.set_markup( | ||||
|             `<span size="larger"><b>${title}</b></span>`); | ||||
|     } | ||||
|  | ||||
|     _isEditedAction(type, number, dir) { | ||||
|         if (!this._editedAction) | ||||
|             return false; | ||||
|  | ||||
|         return (this._editedAction.type == type && | ||||
|         return this._editedAction.type == type && | ||||
|                 this._editedAction.number == number && | ||||
|                 this._editedAction.dir == dir); | ||||
|                 this._editedAction.dir == dir; | ||||
|     } | ||||
|  | ||||
|     _followUpActionEdition(str) { | ||||
|         let { type, dir, number, mode } = this._editedAction; | ||||
|         let hasNextAction = (type == Meta.PadActionType.RING && dir == CCW || | ||||
|                              type == Meta.PadActionType.STRIP && dir == UP); | ||||
|         let hasNextAction = type == Meta.PadActionType.RING && dir == CCW || | ||||
|                              type == Meta.PadActionType.STRIP && dir == UP; | ||||
|         if (!hasNextAction) | ||||
|             return false; | ||||
|  | ||||
| @@ -918,12 +931,8 @@ var PadOsd = class { | ||||
|         this._syncEditionMode(); | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|         this.actor.destroy(); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
|         Main.popModal(this.actor); | ||||
|         Main.popModal(this); | ||||
|         this._actionEditor.close(); | ||||
|  | ||||
|         let deviceManager = Clutter.DeviceManager.get_default(); | ||||
| @@ -941,11 +950,9 @@ var PadOsd = class { | ||||
|             this._capturedEventId = 0; | ||||
|         } | ||||
|  | ||||
|         this.actor = null; | ||||
|         this.emit('closed'); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(PadOsd.prototype); | ||||
| }); | ||||
|  | ||||
| const PadOsdIface = loadInterfaceXML('org.gnome.Shell.Wacom.PadOsd'); | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,15 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported PageIndicators, AnimatedPageIndicators */ | ||||
|  | ||||
| const { Clutter, GLib, GObject, Meta, St } = imports.gi; | ||||
| const { Clutter, GLib, Graphene, GObject, Meta, St } = imports.gi; | ||||
|  | ||||
| const { ANIMATION_TIME_OUT, ANIMATION_MAX_DELAY_OUT_FOR_ITEM, AnimationDirection } = imports.ui.iconGrid; | ||||
|  | ||||
| const INDICATOR_INACTIVE_OPACITY = 128; | ||||
| const INDICATOR_INACTIVE_OPACITY_HOVER = 255; | ||||
| const INDICATOR_INACTIVE_SCALE = 2 / 3; | ||||
| const INDICATOR_INACTIVE_SCALE_PRESSED = 0.5; | ||||
|  | ||||
| var INDICATORS_BASE_TIME = 250; | ||||
| var INDICATORS_BASE_TIME_OUT = 125; | ||||
| var INDICATORS_ANIMATION_DELAY = 125; | ||||
| @@ -12,24 +17,27 @@ var INDICATORS_ANIMATION_DELAY_OUT = 62.5; | ||||
| var INDICATORS_ANIMATION_MAX_TIME = 750; | ||||
| var SWITCH_TIME = 400; | ||||
| var INDICATORS_ANIMATION_MAX_TIME_OUT = | ||||
|     Math.min (SWITCH_TIME, | ||||
|               ANIMATION_TIME_OUT + ANIMATION_MAX_DELAY_OUT_FOR_ITEM); | ||||
|     Math.min(SWITCH_TIME, | ||||
|              ANIMATION_TIME_OUT + ANIMATION_MAX_DELAY_OUT_FOR_ITEM); | ||||
|  | ||||
| var ANIMATION_DELAY = 100; | ||||
|  | ||||
| var PageIndicators = GObject.registerClass({ | ||||
|     Signals: { 'page-activated': { param_types: [GObject.TYPE_INT] } } | ||||
|     Signals: { 'page-activated': { param_types: [GObject.TYPE_INT] } }, | ||||
| }, class PageIndicators extends St.BoxLayout { | ||||
|     _init(vertical = true) { | ||||
|         super._init({ style_class: 'page-indicators', | ||||
|                       vertical, | ||||
|                       x_expand: true, y_expand: true, | ||||
|                       x_align: vertical ? Clutter.ActorAlign.END : Clutter.ActorAlign.CENTER, | ||||
|                       y_align: vertical ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.END, | ||||
|                       reactive: true, | ||||
|                       clip_to_allocation: true }); | ||||
|     _init(orientation = Clutter.Orientation.VERTICAL) { | ||||
|         let vertical = orientation == Clutter.Orientation.VERTICAL; | ||||
|         super._init({ | ||||
|             style_class: 'page-indicators', | ||||
|             vertical, | ||||
|             x_expand: true, y_expand: true, | ||||
|             x_align: vertical ? Clutter.ActorAlign.END : Clutter.ActorAlign.CENTER, | ||||
|             y_align: vertical ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.END, | ||||
|             reactive: true, | ||||
|             clip_to_allocation: true, | ||||
|         }); | ||||
|         this._nPages = 0; | ||||
|         this._currentPage = undefined; | ||||
|         this._currentPosition = 0; | ||||
|         this._reactive = true; | ||||
|         this._reactive = true; | ||||
|     } | ||||
| @@ -63,13 +71,21 @@ var PageIndicators = GObject.registerClass({ | ||||
|                                                 button_mask: St.ButtonMask.ONE | | ||||
|                                                              St.ButtonMask.TWO | | ||||
|                                                              St.ButtonMask.THREE, | ||||
|                                                 toggle_mode: true, | ||||
|                                                 reactive: this._reactive, | ||||
|                                                 checked: pageIndex == this._currentPage }); | ||||
|                 indicator.child = new St.Widget({ style_class: 'page-indicator-icon' }); | ||||
|                                                 reactive: this._reactive }); | ||||
|                 indicator.child = new St.Widget({ | ||||
|                     style_class: 'page-indicator-icon', | ||||
|                     pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), | ||||
|                 }); | ||||
|                 indicator.connect('clicked', () => { | ||||
|                     this.emit('page-activated', pageIndex); | ||||
|                 }); | ||||
|                 indicator.connect('notify::hover', () => { | ||||
|                     this._updateIndicator(indicator, pageIndex); | ||||
|                 }); | ||||
|                 indicator.connect('notify::pressed', () => { | ||||
|                     this._updateIndicator(indicator, pageIndex); | ||||
|                 }); | ||||
|                 this._updateIndicator(indicator, pageIndex); | ||||
|                 this.add_actor(indicator); | ||||
|             } | ||||
|         } else { | ||||
| @@ -78,33 +94,57 @@ var PageIndicators = GObject.registerClass({ | ||||
|                 children[i].destroy(); | ||||
|         } | ||||
|         this._nPages = nPages; | ||||
|         this.visible = (this._nPages > 1); | ||||
|         this.visible = this._nPages > 1; | ||||
|     } | ||||
|  | ||||
|     setCurrentPage(currentPage) { | ||||
|         this._currentPage = currentPage; | ||||
|     _updateIndicator(indicator, pageIndex) { | ||||
|         let progress = | ||||
|             Math.max(1 - Math.abs(this._currentPosition - pageIndex), 0); | ||||
|  | ||||
|         let inactiveScale = indicator.pressed | ||||
|             ? INDICATOR_INACTIVE_SCALE_PRESSED : INDICATOR_INACTIVE_SCALE; | ||||
|         let inactiveOpacity = indicator.hover | ||||
|             ? INDICATOR_INACTIVE_OPACITY_HOVER : INDICATOR_INACTIVE_OPACITY; | ||||
|  | ||||
|         let scale = inactiveScale + (1 - inactiveScale) * progress; | ||||
|         let opacity = inactiveOpacity + (255 - inactiveOpacity) * progress; | ||||
|  | ||||
|         indicator.child.set_scale(scale, scale); | ||||
|         indicator.child.opacity = opacity; | ||||
|     } | ||||
|  | ||||
|     setCurrentPosition(currentPosition) { | ||||
|         this._currentPosition = currentPosition; | ||||
|  | ||||
|         let children = this.get_children(); | ||||
|         for (let i = 0; i < children.length; i++) | ||||
|             children[i].set_checked(i == this._currentPage); | ||||
|             this._updateIndicator(children[i], i); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var AnimatedPageIndicators = GObject.registerClass( | ||||
| class AnimatedPageIndicators extends PageIndicators { | ||||
|     _init() { | ||||
|         super._init(true); | ||||
|         super._init(); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|         this.connect('notify::mapped', () => { | ||||
|             if (!this.mapped) | ||||
|                 return; | ||||
|     _onDestroy() { | ||||
|         if (this.animateLater) { | ||||
|             Meta.later_remove(this.animateLater); | ||||
|             this.animateLater = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|             // Implicit animations are skipped for unmapped actors, and our | ||||
|             // children aren't mapped yet, so defer to a later handler | ||||
|             Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { | ||||
|                 this.animateIndicators(AnimationDirection.IN); | ||||
|                 return GLib.SOURCE_REMOVE; | ||||
|             }); | ||||
|     vfunc_map() { | ||||
|         super.vfunc_map(); | ||||
|  | ||||
|         // Implicit animations are skipped for unmapped actors, and our | ||||
|         // children aren't mapped yet, so defer to a later handler | ||||
|         this.animateLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { | ||||
|             this.animateLater = 0; | ||||
|             this.animateIndicators(AnimationDirection.IN); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -143,7 +183,7 @@ class AnimatedPageIndicators extends PageIndicators { | ||||
|                 translation_x: isAnimationIn ? 0 : offset, | ||||
|                 duration: baseTime + delay * i, | ||||
|                 mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD, | ||||
|                 delay: isAnimationIn ? ANIMATION_DELAY : 0 | ||||
|                 delay: isAnimationIn ? ANIMATION_DELAY : 0, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|   | ||||
							
								
								
									
										236
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								js/ui/panel.js
									
									
									
									
									
								
							| @@ -57,8 +57,7 @@ function _unpremultiply(color) { | ||||
|     let red = Math.min((color.red * 255 + 127) / color.alpha, 255); | ||||
|     let green = Math.min((color.green * 255 + 127) / color.alpha, 255); | ||||
|     let blue = Math.min((color.blue * 255 + 127) / color.alpha, 255); | ||||
|     return new Clutter.Color({ red: red, green: green, | ||||
|                                blue: blue, alpha: color.alpha }); | ||||
|     return new Clutter.Color({ red, green, blue, alpha: color.alpha }); | ||||
| } | ||||
|  | ||||
| class AppMenu extends PopupMenu.PopupMenu { | ||||
| @@ -119,7 +118,7 @@ class AppMenu extends PopupMenu.PopupMenu { | ||||
|  | ||||
|     _updateDetailsVisibility() { | ||||
|         let sw = this._appSystem.lookup_app('org.gnome.Software.desktop'); | ||||
|         this._detailsItem.visible = (sw != null); | ||||
|         this._detailsItem.visible = sw != null; | ||||
|     } | ||||
|  | ||||
|     isEmpty() { | ||||
| @@ -170,9 +169,13 @@ class AppMenu extends PopupMenu.PopupMenu { | ||||
|         let windows = this._app.get_windows(); | ||||
|         windows.forEach(window => { | ||||
|             let title = window.title || this._app.get_name(); | ||||
|             this._windowSection.addAction(title, event => { | ||||
|             let item = this._windowSection.addAction(title, event => { | ||||
|                 Main.activateWindow(window, event.get_time()); | ||||
|             }); | ||||
|             let id = window.connect('notify::title', () => { | ||||
|                 item.label.text = window.title || this._app.get_name(); | ||||
|             }); | ||||
|             item.connect('destroy', () => window.disconnect(id)); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @@ -234,8 +237,11 @@ var AppMenuButton = GObject.registerClass({ | ||||
|         this._overviewHidingId = Main.overview.connect('hiding', this._sync.bind(this)); | ||||
|         this._overviewShowingId = Main.overview.connect('showing', this._sync.bind(this)); | ||||
|  | ||||
|         this._spinner = new Animation.Spinner(PANEL_ICON_SIZE, true); | ||||
|         this._container.add_actor(this._spinner.actor); | ||||
|         this._spinner = new Animation.Spinner(PANEL_ICON_SIZE, { | ||||
|             animate: true, | ||||
|             hideOnStop: true, | ||||
|         }); | ||||
|         this._container.add_actor(this._spinner); | ||||
|  | ||||
|         let menu = new AppMenu(this); | ||||
|         this.setMenu(menu); | ||||
| @@ -264,7 +270,7 @@ var AppMenuButton = GObject.registerClass({ | ||||
|         this.ease({ | ||||
|             opacity: 255, | ||||
|             duration: Overview.ANIMATION_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -279,7 +285,7 @@ var AppMenuButton = GObject.registerClass({ | ||||
|             opacity: 0, | ||||
|             mode: Clutter.Animation.EASE_OUT_QUAD, | ||||
|             duration: Overview.ANIMATION_TIME, | ||||
|             onComplete: () => this.hide() | ||||
|             onComplete: () => this.hide(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -340,9 +346,10 @@ var AppMenuButton = GObject.registerClass({ | ||||
|         if (focusedApp && focusedApp.is_on_workspace(workspace)) | ||||
|             return focusedApp; | ||||
|  | ||||
|         for (let i = 0; i < this._startingApps.length; i++) | ||||
|         for (let i = 0; i < this._startingApps.length; i++) { | ||||
|             if (this._startingApps[i].is_on_workspace(workspace)) | ||||
|                 return this._startingApps[i]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| @@ -365,21 +372,21 @@ var AppMenuButton = GObject.registerClass({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let visible = (this._targetApp != null && !Main.overview.visibleTarget); | ||||
|         let visible = this._targetApp != null && !Main.overview.visibleTarget; | ||||
|         if (visible) | ||||
|             this.fadeIn(); | ||||
|         else | ||||
|             this.fadeOut(); | ||||
|  | ||||
|         let isBusy = (this._targetApp != null && | ||||
|         let isBusy = this._targetApp != null && | ||||
|                       (this._targetApp.get_state() == Shell.AppState.STARTING || | ||||
|                        this._targetApp.get_busy())); | ||||
|                        this._targetApp.get_busy()); | ||||
|         if (isBusy) | ||||
|             this.startAnimation(); | ||||
|         else | ||||
|             this.stopAnimation(); | ||||
|  | ||||
|         this.reactive = (visible && !isBusy); | ||||
|         this.reactive = visible && !isBusy; | ||||
|  | ||||
|         this._syncIcon(); | ||||
|         this.menu.setApp(this._targetApp); | ||||
| @@ -430,16 +437,13 @@ class ActivitiesButton extends PanelMenu.Button { | ||||
|  | ||||
|         this.label_actor = this._label; | ||||
|  | ||||
|         this.connect('captured-event', this._onCapturedEvent.bind(this)); | ||||
|         this.connect_after('key-release-event', this._onKeyRelease.bind(this)); | ||||
|  | ||||
|         Main.overview.connect('showing', () => { | ||||
|             this.add_style_pseudo_class('overview'); | ||||
|             this.add_accessible_state (Atk.StateType.CHECKED); | ||||
|             this.add_accessible_state(Atk.StateType.CHECKED); | ||||
|         }); | ||||
|         Main.overview.connect('hiding', () => { | ||||
|             this.remove_style_pseudo_class('overview'); | ||||
|             this.remove_accessible_state (Atk.StateType.CHECKED); | ||||
|             this.remove_accessible_state(Atk.StateType.CHECKED); | ||||
|         }); | ||||
|  | ||||
|         this._xdndTimeOut = 0; | ||||
| @@ -459,7 +463,7 @@ class ActivitiesButton extends PanelMenu.Button { | ||||
|         return DND.DragMotionResult.CONTINUE; | ||||
|     } | ||||
|  | ||||
|     _onCapturedEvent(actor, event) { | ||||
|     vfunc_captured_event(event) { | ||||
|         if (event.type() == Clutter.EventType.BUTTON_PRESS || | ||||
|             event.type() == Clutter.EventType.TOUCH_BEGIN) { | ||||
|             if (!Main.overview.shouldToggleByCornerOrButton()) | ||||
| @@ -468,23 +472,25 @@ class ActivitiesButton extends PanelMenu.Button { | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onEvent(actor, event) { | ||||
|         super._onEvent(actor, event); | ||||
|  | ||||
|     vfunc_event(event) { | ||||
|         if (event.type() == Clutter.EventType.TOUCH_END || | ||||
|             event.type() == Clutter.EventType.BUTTON_RELEASE) | ||||
|             event.type() == Clutter.EventType.BUTTON_RELEASE) { | ||||
|             if (Main.overview.shouldToggleByCornerOrButton()) | ||||
|                 Main.overview.toggle(); | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onKeyRelease(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|     vfunc_key_release_event(keyEvent) { | ||||
|         let symbol = keyEvent.keyval; | ||||
|         if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) { | ||||
|             if (Main.overview.shouldToggleByCornerOrButton()) | ||||
|             if (Main.overview.shouldToggleByCornerOrButton()) { | ||||
|                 Main.overview.toggle(); | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
| @@ -501,13 +507,12 @@ class ActivitiesButton extends PanelMenu.Button { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| var PanelCorner = class { | ||||
|     constructor(side) { | ||||
| var PanelCorner = GObject.registerClass( | ||||
| class PanelCorner extends St.DrawingArea { | ||||
|     _init(side) { | ||||
|         this._side = side; | ||||
|  | ||||
|         this.actor = new St.DrawingArea({ style_class: 'panel-corner' }); | ||||
|         this.actor.connect('style-changed', this._styleChanged.bind(this)); | ||||
|         this.actor.connect('repaint', this._repaint.bind(this)); | ||||
|         super._init({ style_class: 'panel-corner' }); | ||||
|     } | ||||
|  | ||||
|     _findRightmostButton(container) { | ||||
| @@ -528,8 +533,8 @@ var PanelCorner = class { | ||||
|         if (index < 0) | ||||
|             return null; | ||||
|  | ||||
|         if (!(children[index].has_style_class_name('panel-menu')) && | ||||
|             !(children[index].has_style_class_name('panel-button'))) | ||||
|         if (!children[index].has_style_class_name('panel-menu') && | ||||
|             !children[index].has_style_class_name('panel-button')) | ||||
|             return this._findRightmostButton(children[index]); | ||||
|  | ||||
|         return children[index]; | ||||
| @@ -553,8 +558,8 @@ var PanelCorner = class { | ||||
|         if (index == children.length) | ||||
|             return null; | ||||
|  | ||||
|         if (!(children[index].has_style_class_name('panel-menu')) && | ||||
|             !(children[index].has_style_class_name('panel-button'))) | ||||
|         if (!children[index].has_style_class_name('panel-menu') && | ||||
|             !children[index].has_style_class_name('panel-button')) | ||||
|             return this._findLeftmostButton(children[index]); | ||||
|  | ||||
|         return children[index]; | ||||
| @@ -597,7 +602,7 @@ var PanelCorner = class { | ||||
|             this._buttonStyleChangedSignalId = button.connect('style-changed', | ||||
|                 () => { | ||||
|                     let pseudoClass = button.get_style_pseudo_class(); | ||||
|                     this.actor.set_style_pseudo_class(pseudoClass); | ||||
|                     this.set_style_pseudo_class(pseudoClass); | ||||
|                 }); | ||||
|  | ||||
|             // The corner doesn't support theme transitions, so override | ||||
| @@ -606,8 +611,8 @@ var PanelCorner = class { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _repaint() { | ||||
|         let node = this.actor.get_theme_node(); | ||||
|     vfunc_repaint() { | ||||
|         let node = this.get_theme_node(); | ||||
|  | ||||
|         let cornerRadius = node.get_length("-panel-corner-radius"); | ||||
|         let borderWidth = node.get_length('-panel-corner-border-width'); | ||||
| @@ -618,18 +623,19 @@ var PanelCorner = class { | ||||
|         let overlap = borderColor.alpha != 0; | ||||
|         let offsetY = overlap ? 0 : borderWidth; | ||||
|  | ||||
|         let cr = this.actor.get_context(); | ||||
|         let cr = this.get_context(); | ||||
|         cr.setOperator(Cairo.Operator.SOURCE); | ||||
|  | ||||
|         cr.moveTo(0, offsetY); | ||||
|         if (this._side == St.Side.LEFT) | ||||
|         if (this._side == St.Side.LEFT) { | ||||
|             cr.arc(cornerRadius, | ||||
|                    borderWidth + cornerRadius, | ||||
|                    cornerRadius, Math.PI, 3 * Math.PI / 2); | ||||
|         else | ||||
|         } else { | ||||
|             cr.arc(0, | ||||
|                    borderWidth + cornerRadius, | ||||
|                    cornerRadius, 3 * Math.PI / 2, 2 * Math.PI); | ||||
|         } | ||||
|         cr.lineTo(cornerRadius, offsetY); | ||||
|         cr.closePath(); | ||||
|  | ||||
| @@ -645,7 +651,7 @@ var PanelCorner = class { | ||||
|             Clutter.cairo_set_source_color(cr, backgroundColor); | ||||
|  | ||||
|             cr.save(); | ||||
|             cr.translate(xOffsetDirection * offset, - offset); | ||||
|             cr.translate(xOffsetDirection * offset, -offset); | ||||
|             cr.appendPath(savedPath); | ||||
|             cr.fill(); | ||||
|             cr.restore(); | ||||
| @@ -654,16 +660,17 @@ var PanelCorner = class { | ||||
|         cr.$dispose(); | ||||
|     } | ||||
|  | ||||
|     _styleChanged() { | ||||
|         let node = this.actor.get_theme_node(); | ||||
|     vfunc_style_changed() { | ||||
|         super.vfunc_style_changed(); | ||||
|         let node = this.get_theme_node(); | ||||
|  | ||||
|         let cornerRadius = node.get_length("-panel-corner-radius"); | ||||
|         let borderWidth = node.get_length('-panel-corner-border-width'); | ||||
|  | ||||
|         this.actor.set_size(cornerRadius, borderWidth + cornerRadius); | ||||
|         this.actor.set_anchor_point(0, borderWidth); | ||||
|         this.set_size(cornerRadius, borderWidth + cornerRadius); | ||||
|         this.set_anchor_point(0, borderWidth); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var AggregateLayout = GObject.registerClass( | ||||
| class AggregateLayout extends Clutter.BoxLayout { | ||||
| @@ -706,16 +713,15 @@ class AggregateMenu extends PanelMenu.Button { | ||||
|         this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' }); | ||||
|         this.add_child(this._indicators); | ||||
|  | ||||
|         if (Config.HAVE_NETWORKMANAGER) { | ||||
|         if (Config.HAVE_NETWORKMANAGER) | ||||
|             this._network = new imports.ui.status.network.NMApplet(); | ||||
|         } else { | ||||
|         else | ||||
|             this._network = null; | ||||
|         } | ||||
|         if (Config.HAVE_BLUETOOTH) { | ||||
|  | ||||
|         if (Config.HAVE_BLUETOOTH) | ||||
|             this._bluetooth = new imports.ui.status.bluetooth.Indicator(); | ||||
|         } else { | ||||
|         else | ||||
|             this._bluetooth = null; | ||||
|         } | ||||
|  | ||||
|         this._remoteAccess = new imports.ui.status.remoteAccess.RemoteAccessApplet(); | ||||
|         this._power = new imports.ui.status.power.Indicator(); | ||||
| @@ -728,42 +734,41 @@ class AggregateMenu extends PanelMenu.Button { | ||||
|         this._nightLight = new imports.ui.status.nightLight.Indicator(); | ||||
|         this._thunderbolt = new imports.ui.status.thunderbolt.Indicator(); | ||||
|  | ||||
|         this._indicators.add_child(this._thunderbolt.indicators); | ||||
|         this._indicators.add_child(this._screencast.indicators); | ||||
|         this._indicators.add_child(this._location.indicators); | ||||
|         this._indicators.add_child(this._nightLight.indicators); | ||||
|         if (this._network) { | ||||
|             this._indicators.add_child(this._network.indicators); | ||||
|         } | ||||
|         if (this._bluetooth) { | ||||
|             this._indicators.add_child(this._bluetooth.indicators); | ||||
|         } | ||||
|         this._indicators.add_child(this._remoteAccess.indicators); | ||||
|         this._indicators.add_child(this._rfkill.indicators); | ||||
|         this._indicators.add_child(this._volume.indicators); | ||||
|         this._indicators.add_child(this._power.indicators); | ||||
|         this._indicators.add_child(this._thunderbolt); | ||||
|         this._indicators.add_child(this._screencast); | ||||
|         this._indicators.add_child(this._location); | ||||
|         this._indicators.add_child(this._nightLight); | ||||
|         if (this._network) | ||||
|             this._indicators.add_child(this._network); | ||||
|         if (this._bluetooth) | ||||
|             this._indicators.add_child(this._bluetooth); | ||||
|         this._indicators.add_child(this._remoteAccess); | ||||
|         this._indicators.add_child(this._rfkill); | ||||
|         this._indicators.add_child(this._volume); | ||||
|         this._indicators.add_child(this._power); | ||||
|         this._indicators.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); | ||||
|  | ||||
|         this.menu.addMenuItem(this._volume.menu); | ||||
|         this.menu.addMenuItem(this._brightness.menu); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         if (this._network) { | ||||
|         if (this._network) | ||||
|             this.menu.addMenuItem(this._network.menu); | ||||
|         } | ||||
|         if (this._bluetooth) { | ||||
|  | ||||
|         if (this._bluetooth) | ||||
|             this.menu.addMenuItem(this._bluetooth.menu); | ||||
|         } | ||||
|  | ||||
|         this.menu.addMenuItem(this._remoteAccess.menu); | ||||
|         this.menu.addMenuItem(this._location.menu); | ||||
|         this.menu.addMenuItem(this._rfkill.menu); | ||||
|         this.menu.addMenuItem(this._power.menu); | ||||
|         this.menu.addMenuItem(this._nightLight.menu); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addMenuItem(this._system.menu); | ||||
|  | ||||
|         menuLayout.addSizeChild(this._location.menu.actor); | ||||
|         menuLayout.addSizeChild(this._rfkill.menu.actor); | ||||
|         menuLayout.addSizeChild(this._power.menu.actor); | ||||
|         menuLayout.addSizeChild(this._system.buttonGroup); | ||||
|         menuLayout.addSizeChild(this._system.menu.actor); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -799,14 +804,10 @@ class Panel extends St.Widget { | ||||
|         this.add_child(this._rightBox); | ||||
|  | ||||
|         this._leftCorner = new PanelCorner(St.Side.LEFT); | ||||
|         this.add_child(this._leftCorner.actor); | ||||
|         this.add_child(this._leftCorner); | ||||
|  | ||||
|         this._rightCorner = new PanelCorner(St.Side.RIGHT); | ||||
|         this.add_child(this._rightCorner.actor); | ||||
|  | ||||
|         this.connect('button-press-event', this._onButtonPress.bind(this)); | ||||
|         this.connect('touch-event', this._onButtonPress.bind(this)); | ||||
|         this.connect('key-press-event', this._onKeyPress.bind(this)); | ||||
|         this.add_child(this._rightCorner); | ||||
|  | ||||
|         Main.overview.connect('showing', () => { | ||||
|             this.add_style_pseudo_class('overview'); | ||||
| @@ -895,62 +896,65 @@ class Panel extends St.Widget { | ||||
|  | ||||
|         let cornerWidth, cornerHeight; | ||||
|  | ||||
|         [, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1); | ||||
|         [, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1); | ||||
|         [, cornerWidth] = this._leftCorner.get_preferred_width(-1); | ||||
|         [, cornerHeight] = this._leftCorner.get_preferred_height(-1); | ||||
|         childBox.x1 = 0; | ||||
|         childBox.x2 = cornerWidth; | ||||
|         childBox.y1 = allocHeight; | ||||
|         childBox.y2 = allocHeight + cornerHeight; | ||||
|         this._leftCorner.actor.allocate(childBox, flags); | ||||
|         this._leftCorner.allocate(childBox, flags); | ||||
|  | ||||
|         [, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1); | ||||
|         [, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1); | ||||
|         [, cornerWidth] = this._rightCorner.get_preferred_width(-1); | ||||
|         [, cornerHeight] = this._rightCorner.get_preferred_height(-1); | ||||
|         childBox.x1 = allocWidth - cornerWidth; | ||||
|         childBox.x2 = allocWidth; | ||||
|         childBox.y1 = allocHeight; | ||||
|         childBox.y2 = allocHeight + cornerHeight; | ||||
|         this._rightCorner.actor.allocate(childBox, flags); | ||||
|         this._rightCorner.allocate(childBox, flags); | ||||
|     } | ||||
|  | ||||
|     _onButtonPress(actor, event) { | ||||
|     _tryDragWindow(event) { | ||||
|         if (Main.modalCount > 0) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (event.get_source() != actor) | ||||
|         if (event.source != this) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let type = event.type(); | ||||
|         let isPress = type == Clutter.EventType.BUTTON_PRESS; | ||||
|         if (!isPress && type != Clutter.EventType.TOUCH_BEGIN) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let button = isPress ? event.get_button() : -1; | ||||
|         if (isPress && button != 1) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
|  | ||||
|         let dragWindow = this._getDraggableWindowForPosition(stageX); | ||||
|         let { x, y } = event; | ||||
|         let dragWindow = this._getDraggableWindowForPosition(x); | ||||
|  | ||||
|         if (!dragWindow) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         global.display.begin_grab_op(dragWindow, | ||||
|                                      Meta.GrabOp.MOVING, | ||||
|                                      false, /* pointer grab */ | ||||
|                                      true, /* frame action */ | ||||
|                                      button, | ||||
|                                      event.get_state(), | ||||
|                                      event.get_time(), | ||||
|                                      stageX, stageY); | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|         return global.display.begin_grab_op( | ||||
|             dragWindow, | ||||
|             Meta.GrabOp.MOVING, | ||||
|             false, /* pointer grab */ | ||||
|             true, /* frame action */ | ||||
|             event.button || -1, | ||||
|             event.modifier_state, | ||||
|             event.time, | ||||
|             x, y) ? Clutter.EVENT_STOP : Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onKeyPress(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|     vfunc_button_press_event(buttonEvent) { | ||||
|         if (buttonEvent.button != 1) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         return this._tryDragWindow(buttonEvent); | ||||
|     } | ||||
|  | ||||
|     vfunc_touch_event(touchEvent) { | ||||
|         if (touchEvent.type != Clutter.EventType.TOUCH_BEGIN) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         return this._tryDragWindow(touchEvent); | ||||
|     } | ||||
|  | ||||
|     vfunc_key_press_event(keyEvent) { | ||||
|         let symbol = keyEvent.keyval; | ||||
|         if (symbol == Clutter.KEY_Escape) { | ||||
|             global.display.focus_default_window(event.get_time()); | ||||
|             global.display.focus_default_window(keyEvent.time); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
| @@ -1104,7 +1108,7 @@ class Panel extends St.Widget { | ||||
|         let boxes = { | ||||
|             left: this._leftBox, | ||||
|             center: this._centerBox, | ||||
|             right: this._rightBox | ||||
|             right: this._rightBox, | ||||
|         }; | ||||
|         let boxContainer = boxes[box] || this._rightBox; | ||||
|         this.statusArea[role] = indicator; | ||||
| @@ -1114,14 +1118,14 @@ class Panel extends St.Widget { | ||||
|  | ||||
|     _addStyleClassName(className) { | ||||
|         this.add_style_class_name(className); | ||||
|         this._rightCorner.actor.add_style_class_name(className); | ||||
|         this._leftCorner.actor.add_style_class_name(className); | ||||
|         this._rightCorner.add_style_class_name(className); | ||||
|         this._leftCorner.add_style_class_name(className); | ||||
|     } | ||||
|  | ||||
|     _removeStyleClassName(className) { | ||||
|         this.remove_style_class_name(className); | ||||
|         this._rightCorner.actor.remove_style_class_name(className); | ||||
|         this._leftCorner.actor.remove_style_class_name(className); | ||||
|         this._rightCorner.remove_style_class_name(className); | ||||
|         this._leftCorner.remove_style_class_name(className); | ||||
|     } | ||||
|  | ||||
|     _onMenuSet(indicator) { | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
| /* exported Button, SystemIndicator */ | ||||
|  | ||||
| const { Atk, Clutter, GObject, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| @@ -11,15 +10,17 @@ const PopupMenu = imports.ui.popupMenu; | ||||
| var ButtonBox = GObject.registerClass( | ||||
| class ButtonBox extends St.Widget { | ||||
|     _init(params) { | ||||
|         params = Params.parse(params, { style_class: 'panel-button' }, true); | ||||
|         params = Params.parse(params, { | ||||
|             style_class: 'panel-button', | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }, true); | ||||
|  | ||||
|         super._init(params); | ||||
|  | ||||
|         this._delegate = this; | ||||
|  | ||||
|         this.container = new St.Bin({ y_fill: true, | ||||
|                                       x_fill: true, | ||||
|                                       child: this }); | ||||
|         this.container = new St.Bin({ child: this }); | ||||
|  | ||||
|         this.connect('style-changed', this._onStyleChanged.bind(this)); | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
| @@ -101,9 +102,6 @@ var Button = GObject.registerClass({ | ||||
|                       accessible_name: nameText ? nameText : "", | ||||
|                       accessible_role: Atk.Role.MENU }); | ||||
|  | ||||
|         this.connect('event', this._onEvent.bind(this)); | ||||
|         this.connect('notify::visible', this._onVisibilityChanged.bind(this)); | ||||
|  | ||||
|         if (dontCreateMenu) | ||||
|             this.menu = new PopupMenu.PopupDummyMenu(this); | ||||
|         else | ||||
| @@ -132,7 +130,7 @@ var Button = GObject.registerClass({ | ||||
|         this.emit('menu-set'); | ||||
|     } | ||||
|  | ||||
|     _onEvent(actor, event) { | ||||
|     vfunc_event(event) { | ||||
|         if (this.menu && | ||||
|             (event.type() == Clutter.EventType.TOUCH_BEGIN || | ||||
|              event.type() == Clutter.EventType.BUTTON_PRESS)) | ||||
| @@ -141,11 +139,10 @@ var Button = GObject.registerClass({ | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onVisibilityChanged() { | ||||
|         if (!this.menu) | ||||
|             return; | ||||
|     vfunc_hide() { | ||||
|         super.vfunc_hide(); | ||||
|  | ||||
|         if (!this.visible) | ||||
|         if (this.menu) | ||||
|             this.menu.close(); | ||||
|     } | ||||
|  | ||||
| @@ -157,7 +154,7 @@ var Button = GObject.registerClass({ | ||||
|         if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) { | ||||
|             let group = global.focus_manager.get_group(this); | ||||
|             if (group) { | ||||
|                 let direction = (symbol == Clutter.KEY_Left) ? St.DirectionType.LEFT : St.DirectionType.RIGHT; | ||||
|                 let direction = symbol == Clutter.KEY_Left ? St.DirectionType.LEFT : St.DirectionType.RIGHT; | ||||
|                 group.navigate_focus(this, direction, false); | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
| @@ -182,7 +179,7 @@ var Button = GObject.registerClass({ | ||||
|         // measures are in logical pixels, so make sure to consider the scale | ||||
|         // factor when computing max-height | ||||
|         let maxHeight = Math.round((workArea.height - verticalMargins) / scaleFactor); | ||||
|         this.menu.actor.style = ('max-height: %spx;').format(maxHeight); | ||||
|         this.menu.actor.style = 'max-height: %spx;'.format(maxHeight); | ||||
|     } | ||||
|  | ||||
|     _onDestroy() { | ||||
| @@ -200,24 +197,33 @@ var Button = GObject.registerClass({ | ||||
|  * of an icon and a menu section, which will be composed into the | ||||
|  * aggregate menu. | ||||
|  */ | ||||
| var SystemIndicator = class { | ||||
|     constructor() { | ||||
|         this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box', | ||||
|                                              reactive: true }); | ||||
|         this.indicators.hide(); | ||||
| var SystemIndicator = GObject.registerClass( | ||||
| class SystemIndicator extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             style_class: 'panel-status-indicators-box', | ||||
|             reactive: true, | ||||
|             visible: false, | ||||
|         }); | ||||
|         this.menu = new PopupMenu.PopupMenuSection(); | ||||
|     } | ||||
|  | ||||
|     get indicators() { | ||||
|         let klass = this.constructor.name; | ||||
|         let { stack } = new Error(); | ||||
|         log(`Usage of indicator.indicators is deprecated for ${klass}\n${stack}`); | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     _syncIndicatorsVisible() { | ||||
|         this.indicators.visible = this.indicators.get_children().some(a => a.visible); | ||||
|         this.visible = this.get_children().some(a => a.visible); | ||||
|     } | ||||
|  | ||||
|     _addIndicator() { | ||||
|         let icon = new St.Icon({ style_class: 'system-status-icon' }); | ||||
|         this.indicators.add_actor(icon); | ||||
|         this.add_actor(icon); | ||||
|         icon.connect('notify::visible', this._syncIndicatorsVisible.bind(this)); | ||||
|         this._syncIndicatorsVisible(); | ||||
|         return icon; | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(SystemIndicator.prototype); | ||||
| }); | ||||
|   | ||||
| @@ -10,8 +10,8 @@ var PieTimer = GObject.registerClass({ | ||||
|         'angle': GObject.ParamSpec.double( | ||||
|             'angle', 'angle', 'angle', | ||||
|             GObject.ParamFlags.READWRITE, | ||||
|             0, 2 * Math.PI, 0) | ||||
|     } | ||||
|             0, 2 * Math.PI, 0), | ||||
|     }, | ||||
| }, class PieTimer extends St.DrawingArea { | ||||
|     _init() { | ||||
|         this._angle = 0; | ||||
| @@ -20,7 +20,7 @@ var PieTimer = GObject.registerClass({ | ||||
|             opacity: 0, | ||||
|             visible: false, | ||||
|             can_focus: false, | ||||
|             reactive: false | ||||
|             reactive: false, | ||||
|         }); | ||||
|  | ||||
|         this.set_pivot_point(0.5, 0.5); | ||||
| @@ -84,13 +84,13 @@ var PieTimer = GObject.registerClass({ | ||||
|         this.ease({ | ||||
|             opacity: 255, | ||||
|             duration: duration / 4, | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|         }); | ||||
|  | ||||
|         this.ease_property('angle', 2 * Math.PI, { | ||||
|             duration, | ||||
|             mode: Clutter.AnimationMode.LINEAR, | ||||
|             onComplete: this._onTransitionComplete.bind(this) | ||||
|             onComplete: this._onTransitionComplete.bind(this), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -101,7 +101,7 @@ var PieTimer = GObject.registerClass({ | ||||
|             opacity: 0, | ||||
|             duration: SUCCESS_ZOOM_OUT_DURATION, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onStopped: () => this.destroy() | ||||
|             onStopped: () => this.destroy(), | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
| @@ -110,7 +110,7 @@ var PointerA11yTimeout = class PointerA11yTimeout { | ||||
|     constructor() { | ||||
|         let manager = Clutter.DeviceManager.get_default(); | ||||
|  | ||||
|         manager.connect('ptr-a11y-timeout-started', (manager, device, type, timeout) => { | ||||
|         manager.connect('ptr-a11y-timeout-started', (o, device, type, timeout) => { | ||||
|             let [x, y] = global.get_pointer(); | ||||
|  | ||||
|             this._pieTimer = new PieTimer(); | ||||
| @@ -123,7 +123,7 @@ var PointerA11yTimeout = class PointerA11yTimeout { | ||||
|                 global.display.set_cursor(Meta.Cursor.CROSSHAIR); | ||||
|         }); | ||||
|  | ||||
|         manager.connect('ptr-a11y-timeout-stopped', (manager, device, type, clicked) => { | ||||
|         manager.connect('ptr-a11y-timeout-stopped', (o, device, type, clicked) => { | ||||
|             if (!clicked) | ||||
|                 this._pieTimer.destroy(); | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|             PopupImageMenuItem, PopupMenu, PopupDummyMenu, PopupSubMenu, | ||||
|             PopupMenuSection, PopupSubMenuMenuItem, PopupMenuManager */ | ||||
|  | ||||
| const { Atk, Clutter, Gio, GObject, Shell, St } = imports.gi; | ||||
| const { Atk, Clutter, Gio, GObject, Graphene, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const BoxPointer = imports.ui.boxpointer; | ||||
| @@ -19,14 +19,17 @@ var Ornament = { | ||||
| }; | ||||
|  | ||||
| function isPopupMenuItemVisible(child) { | ||||
|     if (child._delegate instanceof PopupMenuSection) | ||||
|     if (child._delegate instanceof PopupMenuSection) { | ||||
|         if (child._delegate.isEmpty()) | ||||
|             return false; | ||||
|     } | ||||
|     return child.visible; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @side Side to which the arrow points. | ||||
|  * arrowIcon | ||||
|  * @param {St.Side} side - Side to which the arrow points. | ||||
|  * @returns {St.Icon} a new arrow icon | ||||
|  */ | ||||
| function arrowIcon(side) { | ||||
|     let iconName; | ||||
| @@ -65,10 +68,10 @@ var PopupBaseMenuItem = GObject.registerClass({ | ||||
|     }, | ||||
|     Signals: { | ||||
|         'activate': { param_types: [Clutter.Event.$gtype] }, | ||||
|     } | ||||
|     }, | ||||
| }, class PopupBaseMenuItem extends St.BoxLayout { | ||||
|     _init(params) { | ||||
|         params = Params.parse (params, { | ||||
|         params = Params.parse(params, { | ||||
|             reactive: true, | ||||
|             activate: true, | ||||
|             hover: true, | ||||
| @@ -97,12 +100,6 @@ var PopupBaseMenuItem = GObject.registerClass({ | ||||
|         if (params.style_class) | ||||
|             this.add_style_class_name(params.style_class); | ||||
|  | ||||
|         if (this._activatable) { | ||||
|             this.connect('button-press-event', this._onButtonPressEvent.bind(this)); | ||||
|             this.connect('button-release-event', this._onButtonReleaseEvent.bind(this)); | ||||
|             this.connect('touch-event', this._onTouchEvent.bind(this)); | ||||
|             this.connect('key-press-event', this._onKeyPressEvent.bind(this)); | ||||
|         } | ||||
|         if (params.reactive && params.hover) | ||||
|             this.bind_property('hover', this, 'active', GObject.BindingFlags.SYNC_CREATE); | ||||
|     } | ||||
| @@ -124,32 +121,44 @@ var PopupBaseMenuItem = GObject.registerClass({ | ||||
|         this._parent = parent; | ||||
|     } | ||||
|  | ||||
|     _onButtonPressEvent() { | ||||
|     vfunc_button_press_event() { | ||||
|         if (!this._activatable) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         // This is the CSS active state | ||||
|         this.add_style_pseudo_class('active'); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onButtonReleaseEvent(actor, event) { | ||||
|     vfunc_button_release_event() { | ||||
|         if (!this._activatable) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this.remove_style_pseudo_class('active'); | ||||
|         this.activate(event); | ||||
|         this.activate(Clutter.get_current_event()); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
|  | ||||
|     _onTouchEvent(actor, event) { | ||||
|         if (event.type() == Clutter.EventType.TOUCH_END) { | ||||
|     vfunc_touch_event(touchEvent) { | ||||
|         if (!this._activatable) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (touchEvent.type == Clutter.EventType.TOUCH_END) { | ||||
|             this.remove_style_pseudo_class('active'); | ||||
|             this.activate(event); | ||||
|             this.activate(Clutter.get_current_event()); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (event.type() == Clutter.EventType.TOUCH_BEGIN) { | ||||
|         } else if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN) { | ||||
|             // This is the CSS active state | ||||
|             this.add_style_pseudo_class('active'); | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onKeyPressEvent(actor, event) { | ||||
|         let state = event.get_state(); | ||||
|     vfunc_key_press_event(keyEvent) { | ||||
|         if (!this._activatable) | ||||
|             return super.vfunc_key_press_event(keyEvent); | ||||
|  | ||||
|         let state = keyEvent.modifier_state; | ||||
|  | ||||
|         // if user has a modifier down (except capslock and numlock) | ||||
|         // then don't handle the key press here | ||||
| @@ -160,9 +169,9 @@ var PopupBaseMenuItem = GObject.registerClass({ | ||||
|         if (state) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         let symbol = keyEvent.keyval; | ||||
|         if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { | ||||
|             this.activate(event); | ||||
|             this.activate(Clutter.get_current_event()); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
| @@ -263,7 +272,7 @@ class PopupMenuItem extends PopupBaseMenuItem { | ||||
|     _init(text, params) { | ||||
|         super._init(params); | ||||
|  | ||||
|         this.label = new St.Label({ text: text }); | ||||
|         this.label = new St.Label({ text }); | ||||
|         this.add_child(this.label); | ||||
|         this.label_actor = this.label; | ||||
|     } | ||||
| @@ -285,9 +294,10 @@ class PopupSeparatorMenuItem extends PopupBaseMenuItem { | ||||
|         this._syncVisibility(); | ||||
|  | ||||
|         this._separator = new St.Widget({ style_class: 'popup-separator-menu-item', | ||||
|                                           x_expand: true, | ||||
|                                           y_expand: true, | ||||
|                                           y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.add(this._separator, { expand: true }); | ||||
|         this.add_child(this._separator); | ||||
|     } | ||||
|  | ||||
|     _syncVisibility() { | ||||
| @@ -318,12 +328,12 @@ class Switch extends St.Bin { | ||||
| }); | ||||
|  | ||||
| var PopupSwitchMenuItem = GObject.registerClass({ | ||||
|     Signals: { 'toggled': { param_types: [GObject.TYPE_BOOLEAN] }, }, | ||||
|     Signals: { 'toggled': { param_types: [GObject.TYPE_BOOLEAN] } }, | ||||
| }, class PopupSwitchMenuItem extends PopupBaseMenuItem { | ||||
|     _init(text, active, params) { | ||||
|         super._init(params); | ||||
|  | ||||
|         this.label = new St.Label({ text: text }); | ||||
|         this.label = new St.Label({ text }); | ||||
|         this._switch = new Switch(active); | ||||
|  | ||||
|         this.accessible_role = Atk.Role.CHECK_MENU_ITEM; | ||||
| @@ -332,8 +342,11 @@ var PopupSwitchMenuItem = GObject.registerClass({ | ||||
|  | ||||
|         this.add_child(this.label); | ||||
|  | ||||
|         this._statusBin = new St.Bin({ x_align: St.Align.END }); | ||||
|         this.add(this._statusBin, { expand: true, x_align: St.Align.END }); | ||||
|         this._statusBin = new St.Bin({ | ||||
|             x_align: Clutter.ActorAlign.END, | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         this.add_child(this._statusBin); | ||||
|  | ||||
|         this._statusLabel = new St.Label({ | ||||
|             text: '', | ||||
| @@ -406,7 +419,7 @@ class PopupImageMenuItem extends PopupBaseMenuItem { | ||||
|         this._icon = new St.Icon({ style_class: 'popup-menu-icon', | ||||
|                                    x_align: Clutter.ActorAlign.END }); | ||||
|         this.add_child(this._icon); | ||||
|         this.label = new St.Label({ text: text }); | ||||
|         this.label = new St.Label({ text }); | ||||
|         this.add_child(this.label); | ||||
|         this.label_actor = this.label; | ||||
|  | ||||
| @@ -431,12 +444,14 @@ var PopupMenuBase = class { | ||||
|         this.focusActor = sourceActor; | ||||
|         this._parent = null; | ||||
|  | ||||
|         if (styleClass !== undefined) { | ||||
|             this.box = new St.BoxLayout({ style_class: styleClass, | ||||
|                                           vertical: true }); | ||||
|         } else { | ||||
|             this.box = new St.BoxLayout({ vertical: true }); | ||||
|         } | ||||
|         this.box = new St.BoxLayout({ | ||||
|             vertical: true, | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|  | ||||
|         if (styleClass !== undefined) | ||||
|             this.box.style_class = styleClass; | ||||
|         this.length = 0; | ||||
|  | ||||
|         this.isOpen = false; | ||||
| @@ -495,7 +510,7 @@ var PopupMenuBase = class { | ||||
|             menuItem = new PopupMenuItem(title); | ||||
|  | ||||
|         this.addMenuItem(menuItem); | ||||
|         menuItem.connect('activate', (menuItem, event) => { | ||||
|         menuItem.connect('activate', (o, event) => { | ||||
|             callback(event); | ||||
|         }); | ||||
|  | ||||
| @@ -553,7 +568,7 @@ var PopupMenuBase = class { | ||||
|     } | ||||
|  | ||||
|     _connectItemSignals(menuItem) { | ||||
|         menuItem._activeChangeId = menuItem.connect('notify::active', menuItem => { | ||||
|         menuItem._activeChangeId = menuItem.connect('notify::active', () => { | ||||
|             let active = menuItem.active; | ||||
|             if (active && this._activeMenuItem != menuItem) { | ||||
|                 if (this._activeMenuItem) | ||||
| @@ -577,7 +592,7 @@ var PopupMenuBase = class { | ||||
|                     menuItem.actor.grab_key_focus(); | ||||
|             } | ||||
|         }); | ||||
|         menuItem._activateId = menuItem.connect_after('activate', (menuItem, _event) => { | ||||
|         menuItem._activateId = menuItem.connect_after('activate', () => { | ||||
|             this.emit('activate', menuItem); | ||||
|             this.itemActivated(BoxPointer.PopupAnimation.FULL); | ||||
|         }); | ||||
| @@ -590,7 +605,7 @@ var PopupMenuBase = class { | ||||
|         // the menuItem may have, called destroyId | ||||
|         // (FIXME: in the future it may make sense to have container objects | ||||
|         // like PopupMenuManager does) | ||||
|         menuItem._popupMenuDestroyId = menuItem.connect('destroy', menuItem => { | ||||
|         menuItem._popupMenuDestroyId = menuItem.connect('destroy', () => { | ||||
|             menuItem.disconnect(menuItem._popupMenuDestroyId); | ||||
|             menuItem.disconnect(menuItem._activateId); | ||||
|             menuItem.disconnect(menuItem._activeChangeId); | ||||
| @@ -786,10 +801,7 @@ var PopupMenu = class extends PopupMenuBase { | ||||
|         this._arrowAlignment = arrowAlignment; | ||||
|         this._arrowSide = arrowSide; | ||||
|  | ||||
|         this._boxPointer = new BoxPointer.BoxPointer(arrowSide, | ||||
|                                                      { x_fill: true, | ||||
|                                                        y_fill: true, | ||||
|                                                        x_align: St.Align.START }); | ||||
|         this._boxPointer = new BoxPointer.BoxPointer(arrowSide); | ||||
|         this.actor = this._boxPointer; | ||||
|         this.actor._delegate = this; | ||||
|         this.actor.style_class = 'popup-menu-boxpointer'; | ||||
| @@ -800,10 +812,12 @@ var PopupMenu = class extends PopupMenuBase { | ||||
|         global.focus_manager.add_group(this.actor); | ||||
|         this.actor.reactive = true; | ||||
|  | ||||
|         if (this.sourceActor) | ||||
|         if (this.sourceActor) { | ||||
|             this._keyPressId = this.sourceActor.connect('key-press-event', | ||||
|                                                         this._onKeyPress.bind(this)); | ||||
|         } | ||||
|  | ||||
|         this._systemModalOpenedId = 0; | ||||
|         this._openedSubMenu = null; | ||||
|     } | ||||
|  | ||||
| @@ -878,12 +892,17 @@ var PopupMenu = class extends PopupMenuBase { | ||||
|         if (this.isEmpty()) | ||||
|             return; | ||||
|  | ||||
|         if (!this._systemModalOpenedId) { | ||||
|             this._systemModalOpenedId = | ||||
|                 Main.layoutManager.connect('system-modal-opened', () => this.close()); | ||||
|         } | ||||
|  | ||||
|         this.isOpen = true; | ||||
|  | ||||
|         this._boxPointer.setPosition(this.sourceActor, this._arrowAlignment); | ||||
|         this._boxPointer.open(animate); | ||||
|  | ||||
|         this.actor.raise_top(); | ||||
|         this.actor.get_parent().set_child_above_sibling(this.actor, null); | ||||
|  | ||||
|         this.emit('open-state-changed', true); | ||||
|     } | ||||
| @@ -908,6 +927,11 @@ var PopupMenu = class extends PopupMenuBase { | ||||
|     destroy() { | ||||
|         if (this._keyPressId) | ||||
|             this.sourceActor.disconnect(this._keyPressId); | ||||
|  | ||||
|         if (this._systemModalOpenedId) | ||||
|             Main.layoutManager.disconnect(this._systemModalOpenedId); | ||||
|         this._systemModalOpenedId = 0; | ||||
|  | ||||
|         super.destroy(); | ||||
|     } | ||||
| }; | ||||
| @@ -1021,12 +1045,12 @@ var PopupSubMenu = class extends PopupMenuBase { | ||||
|                 height: naturalHeight, | ||||
|                 duration: 250, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_EXPO, | ||||
|                 onComplete: () => this.actor.set_height(-1) | ||||
|                 onComplete: () => this.actor.set_height(-1), | ||||
|             }); | ||||
|             this._arrow.ease({ | ||||
|                 rotation_angle_z: targetAngle, | ||||
|                 duration: 250, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_EXPO | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_EXPO, | ||||
|             }); | ||||
|         } else { | ||||
|             this._arrow.rotation_angle_z = targetAngle; | ||||
| @@ -1054,12 +1078,12 @@ var PopupSubMenu = class extends PopupMenuBase { | ||||
|                 onComplete: () => { | ||||
|                     this.actor.hide(); | ||||
|                     this.actor.set_height(-1); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|             this._arrow.ease({ | ||||
|                 rotation_angle_z: 0, | ||||
|                 duration: 250, | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_EXPO | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_EXPO, | ||||
|             }); | ||||
|         } else { | ||||
|             this._arrow.rotation_angle_z = 0; | ||||
| @@ -1120,17 +1144,20 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem { | ||||
|             this.add_child(this.icon); | ||||
|         } | ||||
|  | ||||
|         this.label = new St.Label({ text: text, | ||||
|         this.label = new St.Label({ text, | ||||
|                                     y_expand: true, | ||||
|                                     y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.add_child(this.label); | ||||
|         this.label_actor = this.label; | ||||
|  | ||||
|         let expander = new St.Bin({ style_class: 'popup-menu-item-expander' }); | ||||
|         this.add(expander, { expand: true }); | ||||
|         let expander = new St.Bin({ | ||||
|             style_class: 'popup-menu-item-expander', | ||||
|             x_expand: true, | ||||
|         }); | ||||
|         this.add_child(expander); | ||||
|  | ||||
|         this._triangle = arrowIcon(St.Side.RIGHT); | ||||
|         this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 }); | ||||
|         this._triangle.pivot_point = new Graphene.Point({ x: 0.5, y: 0.6 }); | ||||
|  | ||||
|         this._triangleBin = new St.Widget({ y_expand: true, | ||||
|                                             y_align: Clutter.ActorAlign.CENTER }); | ||||
| @@ -1165,7 +1192,7 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem { | ||||
|         } else { | ||||
|             this.remove_style_pseudo_class('open'); | ||||
|             this._getTopMenu()._setOpenedSubMenu(null); | ||||
|             this.remove_accessible_state (Atk.StateType.EXPANDED); | ||||
|             this.remove_accessible_state(Atk.StateType.EXPANDED); | ||||
|             this.remove_style_pseudo_class('checked'); | ||||
|         } | ||||
|     } | ||||
| @@ -1185,8 +1212,8 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem { | ||||
|         return this.menu.isOpen; | ||||
|     } | ||||
|  | ||||
|     _onKeyPressEvent(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|     vfunc_key_press_event(keyPressEvent) { | ||||
|         let symbol = keyPressEvent.keyval; | ||||
|  | ||||
|         if (symbol == Clutter.KEY_Right) { | ||||
|             this._setOpenState(true); | ||||
| @@ -1197,14 +1224,14 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem { | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return super._onKeyPressEvent(actor, event); | ||||
|         return super.vfunc_key_press_event(keyPressEvent); | ||||
|     } | ||||
|  | ||||
|     activate(_event) { | ||||
|         this._setOpenState(true); | ||||
|     } | ||||
|  | ||||
|     _onButtonReleaseEvent() { | ||||
|     vfunc_button_release_event() { | ||||
|         // Since we override the parent, we need to manage what the parent does | ||||
|         // with the active style class | ||||
|         this.remove_style_pseudo_class('active'); | ||||
| @@ -1212,8 +1239,8 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem { | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onTouchEvent(actor, event) { | ||||
|         if (event.type() == Clutter.EventType.TOUCH_END) { | ||||
|     vfunc_touch_event(touchEvent) { | ||||
|         if (touchEvent.type == Clutter.EventType.TOUCH_END) { | ||||
|             // Since we override the parent, we need to manage what the parent does | ||||
|             // with the active style class | ||||
|             this.remove_style_pseudo_class('active'); | ||||
| @@ -1239,11 +1266,11 @@ var PopupMenuManager = class { | ||||
|             return; | ||||
|  | ||||
|         let menudata = { | ||||
|             menu:              menu, | ||||
|             menu, | ||||
|             openStateChangeId: menu.connect('open-state-changed', this._onMenuOpenState.bind(this)), | ||||
|             destroyId:         menu.connect('destroy', this._onMenuDestroy.bind(this)), | ||||
|             enterId:           0, | ||||
|             focusInId:         0 | ||||
|             focusInId:         0, | ||||
|         }; | ||||
|  | ||||
|         let source = menu.sourceActor; | ||||
|   | ||||
| @@ -181,7 +181,7 @@ function loadRemoteSearchProviders(searchSettings, callback) { | ||||
|             return -1; | ||||
|  | ||||
|         // finally, if both providers are found, return their order in the list | ||||
|         return (idxA - idxB); | ||||
|         return idxA - idxB; | ||||
|     }); | ||||
|  | ||||
|     callback(loadedProviders); | ||||
| @@ -228,8 +228,7 @@ var RemoteSearchProvider = class { | ||||
|         } | ||||
|  | ||||
|         if (gicon) | ||||
|             icon = new St.Icon({ gicon: gicon, | ||||
|                                  icon_size: size }); | ||||
|             icon = new St.Icon({ gicon, icon_size: size }); | ||||
|         return icon; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -53,13 +53,13 @@ var Ripples = class Ripples { | ||||
|         ripple.visible = true; | ||||
|         ripple.opacity = 255 * Math.sqrt(startOpacity); | ||||
|         ripple.scale_x = ripple.scale_y = startScale; | ||||
|         ripple.set_translation( - this._px * ripple.width, - this._py * ripple.height, 0.0); | ||||
|         ripple.set_translation(-this._px * ripple.width, -this._py * ripple.height, 0.0); | ||||
|  | ||||
|         ripple.ease({ | ||||
|             opacity: 0, | ||||
|             delay, | ||||
|             duration, | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD | ||||
|             mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|         }); | ||||
|         ripple.ease({ | ||||
|             scale_x: finalScale, | ||||
| @@ -67,7 +67,7 @@ var Ripples = class Ripples { | ||||
|             delay, | ||||
|             duration, | ||||
|             mode: Clutter.AnimationMode.LINEAR, | ||||
|             onComplete: () => (ripple.visible = false) | ||||
|             onComplete: () => (ripple.visible = false), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -57,9 +57,7 @@ class RunDialog extends ModalDialog.ModalDialog { | ||||
|         let label = new St.Label({ style_class: 'run-dialog-label', | ||||
|                                    text: _("Enter a Command") }); | ||||
|  | ||||
|         this.contentLayout.add(label, { x_fill: false, | ||||
|                                         x_align: St.Align.START, | ||||
|                                         y_align: St.Align.START }); | ||||
|         this.contentLayout.add_child(label); | ||||
|  | ||||
|         let entry = new St.Entry({ style_class: 'run-dialog-entry', | ||||
|                                    can_focus: true }); | ||||
| @@ -68,36 +66,36 @@ class RunDialog extends ModalDialog.ModalDialog { | ||||
|         entry.label_actor = label; | ||||
|  | ||||
|         this._entryText = entry.clutter_text; | ||||
|         this.contentLayout.add(entry, { y_align: St.Align.START }); | ||||
|         this.contentLayout.add_child(entry); | ||||
|         this.setInitialKeyFocus(this._entryText); | ||||
|  | ||||
|         this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' }); | ||||
|  | ||||
|         this.contentLayout.add(this._errorBox, { expand: true }); | ||||
|         this.contentLayout.add_child(this._errorBox); | ||||
|  | ||||
|         let errorIcon = new St.Icon({ icon_name: 'dialog-error-symbolic', | ||||
|                                       icon_size: 24, | ||||
|                                       style_class: 'run-dialog-error-icon' }); | ||||
|                                       style_class: 'run-dialog-error-icon', | ||||
|                                       y_align: Clutter.ActorAlign.CENTER }); | ||||
|  | ||||
|         this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE }); | ||||
|         this._errorBox.add_child(errorIcon); | ||||
|  | ||||
|         this._commandError = false; | ||||
|  | ||||
|         this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' }); | ||||
|         this._errorMessage = new St.Label({ | ||||
|             style_class: 'run-dialog-error-label', | ||||
|             y_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         this._errorMessage.clutter_text.line_wrap = true; | ||||
|  | ||||
|         this._errorBox.add(this._errorMessage, { expand: true, | ||||
|                                                  x_align: St.Align.START, | ||||
|                                                  x_fill: false, | ||||
|                                                  y_align: St.Align.MIDDLE, | ||||
|                                                  y_fill: false }); | ||||
|         this._errorBox.add_child(this._errorMessage); | ||||
|  | ||||
|         this._errorBox.hide(); | ||||
|  | ||||
|         this.setButtons([{ | ||||
|             action: this.close.bind(this), | ||||
|             label: _("Close"), | ||||
|             key: Clutter.Escape, | ||||
|             key: Clutter.KEY_Escape, | ||||
|         }]); | ||||
|  | ||||
|         this._pathCompleter = new Gio.FilenameCompleter(); | ||||
| @@ -114,7 +112,7 @@ class RunDialog extends ModalDialog.ModalDialog { | ||||
|         }); | ||||
|         this._entryText.connect('key-press-event', (o, e) => { | ||||
|             let symbol = e.get_key_symbol(); | ||||
|             if (symbol == Clutter.Tab) { | ||||
|             if (symbol === Clutter.KEY_Tab) { | ||||
|                 let text = o.get_text(); | ||||
|                 let prefix; | ||||
|                 if (text.lastIndexOf(' ') == -1) | ||||
| @@ -177,11 +175,10 @@ class RunDialog extends ModalDialog.ModalDialog { | ||||
|     } | ||||
|  | ||||
|     _getCompletion(text) { | ||||
|         if (text.includes('/')) { | ||||
|         if (text.includes('/')) | ||||
|             return this._pathCompleter.get_completion_suffix(text); | ||||
|         } else { | ||||
|         else | ||||
|             return this._getCommandCompletion(text); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _run(input, inTerminal) { | ||||
| @@ -212,7 +209,7 @@ class RunDialog extends ModalDialog.ModalDialog { | ||||
|                 } else { | ||||
|                     if (input.charAt(0) == '~') | ||||
|                         input = input.slice(1); | ||||
|                     path = GLib.get_home_dir() + '/' + input; | ||||
|                     path = `${GLib.get_home_dir()}/${input}`; | ||||
|                 } | ||||
|  | ||||
|                 if (GLib.file_test(path, GLib.FileTest.EXISTS)) { | ||||
| @@ -220,12 +217,12 @@ class RunDialog extends ModalDialog.ModalDialog { | ||||
|                     try { | ||||
|                         Gio.app_info_launch_default_for_uri(file.get_uri(), | ||||
|                                                             global.create_app_launch_context(0, -1)); | ||||
|                     } catch (e) { | ||||
|                     } catch (err) { | ||||
|                         // The exception from gjs contains an error string like: | ||||
|                         //     Error invoking Gio.app_info_launch_default_for_uri: No application | ||||
|                         //     is registered as handling this file | ||||
|                         // We are only interested in the part after the first colon. | ||||
|                         let message = e.message.replace(/[^:]*: *(.+)/, '$1'); | ||||
|                         let message = err.message.replace(/[^:]*: *(.+)/, '$1'); | ||||
|                         this._showError(message); | ||||
|                     } | ||||
|                 } else { | ||||
| @@ -252,7 +249,7 @@ class RunDialog extends ModalDialog.ModalDialog { | ||||
|                 onComplete: () => { | ||||
|                     parentActor.set_height(-1); | ||||
|                     this._errorBox.show(); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const { AccountsService, Clutter, Cogl, Gio, GLib, | ||||
|         GnomeDesktop, GObject, Meta, Shell, St } = imports.gi; | ||||
| const { AccountsService, Clutter, Gio, GLib, | ||||
|         GnomeDesktop, GObject, Graphene, Meta, Shell, St } = imports.gi; | ||||
| const Cairo = imports.cairo; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| @@ -17,6 +17,8 @@ const MessageTray = imports.ui.messageTray; | ||||
| const ShellDBus = imports.ui.shellDBus; | ||||
| const SmartcardManager = imports.misc.smartcardManager; | ||||
|  | ||||
| const { adjustAnimationTime } = imports.ui.environment; | ||||
|  | ||||
| const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; | ||||
| const LOCK_ENABLED_KEY = 'lock-enabled'; | ||||
| const LOCK_DELAY_KEY = 'lock-delay'; | ||||
| @@ -46,21 +48,29 @@ var STANDARD_FADE_TIME = 10000; | ||||
| var MANUAL_FADE_TIME = 300; | ||||
| var CURTAIN_SLIDE_TIME = 300; | ||||
|  | ||||
| var Clock = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock', | ||||
|                                         vertical: true }); | ||||
| var Clock = GObject.registerClass( | ||||
| class ScreenShieldClock extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ style_class: 'screen-shield-clock', vertical: true }); | ||||
|  | ||||
|         this._time = new St.Label({ style_class: 'screen-shield-clock-time' }); | ||||
|         this._date = new St.Label({ style_class: 'screen-shield-clock-date' }); | ||||
|         this._time = new St.Label({ | ||||
|             style_class: 'screen-shield-clock-time', | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|         this._date = new St.Label({ | ||||
|             style_class: 'screen-shield-clock-date', | ||||
|             x_align: Clutter.ActorAlign.CENTER, | ||||
|         }); | ||||
|  | ||||
|         this.actor.add(this._time, { x_align: St.Align.MIDDLE }); | ||||
|         this.actor.add(this._date, { x_align: St.Align.MIDDLE }); | ||||
|         this.add_child(this._time); | ||||
|         this.add_child(this._date); | ||||
|  | ||||
|         this._wallClock = new GnomeDesktop.WallClock({ time_only: true }); | ||||
|         this._wallClock.connect('notify::clock', this._updateClock.bind(this)); | ||||
|  | ||||
|         this._updateClock(); | ||||
|  | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|     _updateClock() { | ||||
| @@ -73,25 +83,27 @@ var Clock = class { | ||||
|         this._date.text = date.toLocaleFormat(dateFormat); | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|         this.actor.destroy(); | ||||
|     _onDestroy() { | ||||
|         this._wallClock.run_dispose(); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|  | ||||
| var NotificationsBox = class { | ||||
|     constructor() { | ||||
|         this.actor = new St.BoxLayout({ vertical: true, | ||||
|                                         name: 'screenShieldNotifications', | ||||
|                                         style_class: 'screen-shield-notifications-container' }); | ||||
| var NotificationsBox = GObject.registerClass({ | ||||
|     Signals: { 'wake-up-screen': {} }, | ||||
| }, class NotificationsBox extends St.BoxLayout { | ||||
|     _init() { | ||||
|         super._init({ | ||||
|             vertical: true, | ||||
|             name: 'screenShieldNotifications', | ||||
|             style_class: 'screen-shield-notifications-container', | ||||
|         }); | ||||
|  | ||||
|         this._scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START, | ||||
|                                                hscrollbar_policy: St.PolicyType.NEVER }); | ||||
|         this._scrollView = new St.ScrollView({ hscrollbar_policy: St.PolicyType.NEVER }); | ||||
|         this._notificationBox = new St.BoxLayout({ vertical: true, | ||||
|                                                    style_class: 'screen-shield-notifications-container' }); | ||||
|         this._scrollView.add_actor(this._notificationBox); | ||||
|  | ||||
|         this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START }); | ||||
|         this.add_child(this._scrollView); | ||||
|  | ||||
|         this._sources = new Map(); | ||||
|         Main.messageTray.getSources().forEach(source => { | ||||
| @@ -100,27 +112,26 @@ var NotificationsBox = class { | ||||
|         this._updateVisibility(); | ||||
|  | ||||
|         this._sourceAddedId = Main.messageTray.connect('source-added', this._sourceAdded.bind(this)); | ||||
|  | ||||
|         this.connect('destroy', this._onDestroy.bind(this)); | ||||
|     } | ||||
|  | ||||
|     destroy() { | ||||
|     _onDestroy() { | ||||
|         if (this._sourceAddedId) { | ||||
|             Main.messageTray.disconnect(this._sourceAddedId); | ||||
|             this._sourceAddedId = 0; | ||||
|         } | ||||
|  | ||||
|         let items = this._sources.entries(); | ||||
|         for (let [source, obj] of items) { | ||||
|         for (let [source, obj] of items) | ||||
|             this._removeSource(source, obj); | ||||
|         } | ||||
|  | ||||
|         this.actor.destroy(); | ||||
|     } | ||||
|  | ||||
|     _updateVisibility() { | ||||
|         this._notificationBox.visible = | ||||
|             this._notificationBox.get_children().some(a => a.visible); | ||||
|  | ||||
|         this.actor.visible = this._notificationBox.visible; | ||||
|         this.visible = this._notificationBox.visible; | ||||
|     } | ||||
|  | ||||
|     _makeNotificationCountText(count, isChat) { | ||||
| @@ -132,10 +143,10 @@ var NotificationsBox = class { | ||||
|  | ||||
|     _makeNotificationSource(source, box) { | ||||
|         let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); | ||||
|         box.add(sourceActor, { y_fill: true }); | ||||
|         box.add_child(sourceActor); | ||||
|  | ||||
|         let textBox = new St.BoxLayout({ vertical: true }); | ||||
|         box.add(textBox, { y_fill: false, y_align: St.Align.START }); | ||||
|         box.add_child(textBox); | ||||
|  | ||||
|         let title = new St.Label({ text: source.title, | ||||
|                                    style_class: 'screen-shield-notification-label' }); | ||||
| @@ -152,13 +163,11 @@ var NotificationsBox = class { | ||||
|  | ||||
|     _makeNotificationDetailedSource(source, box) { | ||||
|         let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE); | ||||
|         let sourceBin = new St.Bin({ y_align: St.Align.START, | ||||
|                                      x_align: St.Align.START, | ||||
|                                      child: sourceActor }); | ||||
|         let sourceBin = new St.Bin({ child: sourceActor }); | ||||
|         box.add(sourceBin); | ||||
|  | ||||
|         let textBox = new St.BoxLayout({ vertical: true }); | ||||
|         box.add(textBox, { y_fill: false, y_align: St.Align.START }); | ||||
|         box.add_child(textBox); | ||||
|  | ||||
|         let title = new St.Label({ text: source.title, | ||||
|                                    style_class: 'screen-shield-notification-label' }); | ||||
| @@ -179,7 +188,7 @@ var NotificationsBox = class { | ||||
|             } | ||||
|  | ||||
|             let label = new St.Label({ style_class: 'screen-shield-notification-count-text' }); | ||||
|             label.clutter_text.set_markup('<b>' + n.title + '</b> ' + body); | ||||
|             label.clutter_text.set_markup(`<b>${n.title}</b> ${body}`); | ||||
|             textBox.add(label); | ||||
|  | ||||
|             visible = true; | ||||
| @@ -195,11 +204,10 @@ var NotificationsBox = class { | ||||
|     } | ||||
|  | ||||
|     _showSource(source, obj, box) { | ||||
|         if (obj.detailed) { | ||||
|         if (obj.detailed) | ||||
|             [obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box); | ||||
|         } else { | ||||
|         else | ||||
|             [obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box); | ||||
|         } | ||||
|  | ||||
|         box.visible = obj.visible && (source.unseenCount > 0); | ||||
|     } | ||||
| @@ -220,21 +228,21 @@ var NotificationsBox = class { | ||||
|         obj.sourceBox = new St.BoxLayout({ style_class: 'screen-shield-notification-source', | ||||
|                                            x_expand: true }); | ||||
|         this._showSource(source, obj, obj.sourceBox); | ||||
|         this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START }); | ||||
|         this._notificationBox.add_child(obj.sourceBox); | ||||
|  | ||||
|         obj.sourceCountChangedId = source.connect('count-updated', source => { | ||||
|         obj.sourceCountChangedId = source.connect('notify::count', () => { | ||||
|             this._countChanged(source, obj); | ||||
|         }); | ||||
|         obj.sourceTitleChangedId = source.connect('title-changed', source => { | ||||
|         obj.sourceTitleChangedId = source.connect('notify::title', () => { | ||||
|             this._titleChanged(source, obj); | ||||
|         }); | ||||
|         obj.policyChangedId = source.policy.connect('policy-changed', (policy, key) => { | ||||
|             if (key == 'show-in-lock-screen') | ||||
|         obj.policyChangedId = source.policy.connect('notify', (policy, pspec) => { | ||||
|             if (pspec.name == 'show-in-lock-screen') | ||||
|                 this._visibleChanged(source, obj); | ||||
|             else | ||||
|                 this._detailedChanged(source, obj); | ||||
|         }); | ||||
|         obj.sourceDestroyId = source.connect('destroy', source => { | ||||
|         obj.sourceDestroyId = source.connect('destroy', () => { | ||||
|             this._onSourceDestroy(source, obj); | ||||
|         }); | ||||
|  | ||||
| @@ -256,7 +264,7 @@ var NotificationsBox = class { | ||||
|                 onComplete: () => { | ||||
|                     this._scrollView.vscrollbar_policy = St.PolicyType.AUTOMATIC; | ||||
|                     widget.set_height(-1); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|  | ||||
|             this._updateVisibility(); | ||||
| @@ -336,16 +344,17 @@ var NotificationsBox = class { | ||||
|  | ||||
|         this._sources.delete(source); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(NotificationsBox.prototype); | ||||
| }); | ||||
|  | ||||
| var Arrow = GObject.registerClass( | ||||
| class ScreenShieldArrow extends St.Bin { | ||||
|     _init(params) { | ||||
|         super._init(params); | ||||
|         this.x_fill = this.y_fill = true; | ||||
|  | ||||
|         this._drawingArea = new St.DrawingArea(); | ||||
|         this._drawingArea = new St.DrawingArea({ | ||||
|             x_expand: true, | ||||
|             y_expand: true, | ||||
|         }); | ||||
|         this._drawingArea.connect('repaint', this._drawArrow.bind(this)); | ||||
|         this.child = this._drawingArea; | ||||
|  | ||||
| @@ -398,18 +407,18 @@ class ScreenShieldArrow extends St.Bin { | ||||
|         super.vfunc_style_changed(); | ||||
|     } | ||||
|  | ||||
|     vfunc_paint() { | ||||
|     vfunc_paint(paintContext) { | ||||
|         if (this._shadowHelper) { | ||||
|             this._shadowHelper.update(this._drawingArea); | ||||
|  | ||||
|             let allocation = this._drawingArea.get_allocation_box(); | ||||
|             let paintOpacity = this._drawingArea.get_paint_opacity(); | ||||
|             let framebuffer = Cogl.get_draw_framebuffer(); | ||||
|             let framebuffer = paintContext.get_framebuffer(); | ||||
|  | ||||
|             this._shadowHelper.paint(framebuffer, allocation, paintOpacity); | ||||
|         } | ||||
|  | ||||
|         this._drawingArea.paint(); | ||||
|         this._drawingArea.paint(paintContext); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -453,7 +462,7 @@ var ScreenShield = class { | ||||
|         this._backgroundGroup = new Clutter.Actor(); | ||||
|  | ||||
|         this._lockScreenGroup.add_actor(this._backgroundGroup); | ||||
|         this._backgroundGroup.lower_bottom(); | ||||
|         this._lockScreenGroup.set_child_below_sibling(this._backgroundGroup, null); | ||||
|         this._bgManagers = []; | ||||
|  | ||||
|         this._updateBackgrounds(); | ||||
| @@ -486,7 +495,7 @@ var ScreenShield = class { | ||||
|         this._lockDialogGroup = new St.Widget({ x_expand: true, | ||||
|                                                 y_expand: true, | ||||
|                                                 reactive: true, | ||||
|                                                 pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }), | ||||
|                                                 pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), | ||||
|                                                 name: 'lockDialogGroup' }); | ||||
|  | ||||
|         this.actor.add_actor(this._lockDialogGroup); | ||||
| @@ -557,11 +566,11 @@ var ScreenShield = class { | ||||
|         this._longLightbox = new Lightbox.Lightbox(Main.uiGroup, | ||||
|                                                    { inhibitEvents: true, | ||||
|                                                      fadeFactor: 1 }); | ||||
|         this._longLightbox.connect('shown', this._onLongLightboxShown.bind(this)); | ||||
|         this._longLightbox.connect('notify::active', this._onLongLightbox.bind(this)); | ||||
|         this._shortLightbox = new Lightbox.Lightbox(Main.uiGroup, | ||||
|                                                     { inhibitEvents: true, | ||||
|                                                       fadeFactor: 1 }); | ||||
|         this._shortLightbox.connect('shown', this._onShortLightboxShown.bind(this)); | ||||
|         this._shortLightbox.connect('notify::active', this._onShortLightbox.bind(this)); | ||||
|  | ||||
|         this.idleMonitor = Meta.IdleMonitor.get_core(); | ||||
|         this._cursorTracker = Meta.CursorTracker.get_for_display(global.display); | ||||
| @@ -591,7 +600,7 @@ var ScreenShield = class { | ||||
|                                      height: monitor.height }); | ||||
|  | ||||
|         let bgManager = new Background.BackgroundManager({ container: widget, | ||||
|                                                            monitorIndex: monitorIndex, | ||||
|                                                            monitorIndex, | ||||
|                                                            controlPosition: false, | ||||
|                                                            settingsSchema: SCREENSAVER_SCHEMA }); | ||||
|  | ||||
| @@ -662,12 +671,12 @@ var ScreenShield = class { | ||||
|         if (this._lockScreenState != MessageTray.State.SHOWN) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let isEnter = (symbol == Clutter.KEY_Return || | ||||
|         let isEnter = symbol == Clutter.KEY_Return || | ||||
|                        symbol == Clutter.KEY_KP_Enter || | ||||
|                        symbol == Clutter.KEY_ISO_Enter); | ||||
|         let isEscape = (symbol == Clutter.KEY_Escape); | ||||
|         let isLiftChar = (GLib.unichar_isprint(unichar) && | ||||
|                           (this._isLocked || !GLib.unichar_isgraph(unichar))); | ||||
|                        symbol == Clutter.KEY_ISO_Enter; | ||||
|         let isEscape = symbol == Clutter.KEY_Escape; | ||||
|         let isLiftChar = GLib.unichar_isprint(unichar) && | ||||
|                           (this._isLocked || !GLib.unichar_isgraph(unichar)); | ||||
|         if (!isEnter && !isEscape && !isLiftChar) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
| @@ -693,9 +702,8 @@ var ScreenShield = class { | ||||
|         this._lockScreenScrollCounter += delta; | ||||
|  | ||||
|         // 7 standard scrolls to lift up | ||||
|         if (this._lockScreenScrollCounter > 35) { | ||||
|         if (this._lockScreenScrollCounter > 35) | ||||
|             this._liftShield(true, 0); | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| @@ -703,8 +711,8 @@ var ScreenShield = class { | ||||
|     _syncInhibitor() { | ||||
|         let lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY); | ||||
|         let lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY); | ||||
|         let inhibit = (this._loginSession && this._loginSession.Active && | ||||
|                        !this._isActive && lockEnabled && !lockLocked); | ||||
|         let inhibit = this._loginSession && this._loginSession.Active && | ||||
|                        !this._isActive && lockEnabled && !lockLocked; | ||||
|         if (inhibit) { | ||||
|             this._loginManager.inhibit(_("GNOME needs to lock the screen"), | ||||
|                 inhibitor => { | ||||
| @@ -743,9 +751,9 @@ var ScreenShield = class { | ||||
|                     arrows[i].ease({ | ||||
|                         opacity: 0, | ||||
|                         duration: ARROW_ANIMATION_TIME / 2, | ||||
|                         mode: Clutter.AnimationMode.EASE_IN_QUAD | ||||
|                         mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|                     }); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|         } | ||||
|  | ||||
| @@ -785,7 +793,7 @@ var ScreenShield = class { | ||||
|             // restore the lock screen to its original place | ||||
|             // try to use the same speed as the normal animation | ||||
|             let h = global.stage.height; | ||||
|             let duration = MANUAL_FADE_TIME * (-this._lockScreenGroup.y) / h; | ||||
|             let duration = MANUAL_FADE_TIME * -this._lockScreenGroup.y / h; | ||||
|             this._lockScreenGroup.remove_all_transitions(); | ||||
|             this._lockScreenGroup.ease({ | ||||
|                 y: 0, | ||||
| @@ -794,7 +802,7 @@ var ScreenShield = class { | ||||
|                 onComplete: () => { | ||||
|                     this._lockScreenGroup.fixed_position_set = false; | ||||
|                     this._lockScreenState = MessageTray.State.SHOWN; | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|  | ||||
|             this._maybeCancelDialog(); | ||||
| @@ -807,7 +815,7 @@ var ScreenShield = class { | ||||
|  | ||||
|         this._maybeCancelDialog(); | ||||
|  | ||||
|         if (this._longLightbox.actor.visible) { | ||||
|         if (this._longLightbox.visible) { | ||||
|             // We're in the process of showing. | ||||
|             return; | ||||
|         } | ||||
| @@ -832,7 +840,7 @@ var ScreenShield = class { | ||||
|  | ||||
|         if (shouldLock) { | ||||
|             let lockTimeout = Math.max( | ||||
|                 STANDARD_FADE_TIME, | ||||
|                 adjustAnimationTime(STANDARD_FADE_TIME), | ||||
|                 this._settings.get_uint(LOCK_DELAY_KEY) * 1000); | ||||
|             this._lockTimeoutId = GLib.timeout_add( | ||||
|                 GLib.PRIORITY_DEFAULT, | ||||
| @@ -849,8 +857,8 @@ var ScreenShield = class { | ||||
|     } | ||||
|  | ||||
|     _activateFade(lightbox, time) { | ||||
|         Main.uiGroup.set_child_above_sibling(lightbox.actor, null); | ||||
|         lightbox.show(time); | ||||
|         Main.uiGroup.set_child_above_sibling(lightbox, null); | ||||
|         lightbox.lightOn(time); | ||||
|  | ||||
|         if (this._becameActiveId == 0) | ||||
|             this._becameActiveId = this.idleMonitor.add_user_active_watch(this._onUserBecameActive.bind(this)); | ||||
| @@ -879,19 +887,21 @@ var ScreenShield = class { | ||||
|         this._becameActiveId = 0; | ||||
|  | ||||
|         if (this._isActive || this._isLocked) { | ||||
|             this._longLightbox.hide(); | ||||
|             this._shortLightbox.hide(); | ||||
|             this._longLightbox.lightOff(); | ||||
|             this._shortLightbox.lightOff(); | ||||
|         } else { | ||||
|             this.deactivate(false); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _onLongLightboxShown() { | ||||
|         this.activate(false); | ||||
|     _onLongLightbox(lightBox) { | ||||
|         if (lightBox.active) | ||||
|             this.activate(false); | ||||
|     } | ||||
|  | ||||
|     _onShortLightboxShown() { | ||||
|         this._completeLockScreenShown(); | ||||
|     _onShortLightbox(lightBox) { | ||||
|         if (lightBox.active) | ||||
|             this._completeLockScreenShown(); | ||||
|     } | ||||
|  | ||||
|     showDialog() { | ||||
| @@ -935,7 +945,7 @@ var ScreenShield = class { | ||||
|             // use the same speed regardless of original position | ||||
|             // if velocity is specified, it's in pixels per milliseconds | ||||
|             let h = global.stage.height; | ||||
|             let delta = (h + this._lockScreenGroup.y); | ||||
|             let delta = h + this._lockScreenGroup.y; | ||||
|             let minVelocity = global.stage.height / CURTAIN_SLIDE_TIME; | ||||
|  | ||||
|             velocity = Math.max(minVelocity, velocity); | ||||
| @@ -945,7 +955,7 @@ var ScreenShield = class { | ||||
|                 y: -h, | ||||
|                 duration, | ||||
|                 mode: Clutter.AnimationMode.EASE_IN_QUAD, | ||||
|                 onComplete: () => this._hideLockScreenComplete() | ||||
|                 onComplete: () => this._hideLockScreenComplete(), | ||||
|             }); | ||||
|         } else { | ||||
|             this._hideLockScreenComplete(); | ||||
| @@ -965,7 +975,6 @@ var ScreenShield = class { | ||||
|  | ||||
|             this._dialog = new constructor(this._lockDialogGroup); | ||||
|  | ||||
|  | ||||
|             let time = global.get_current_time(); | ||||
|             if (!this._dialog.open(time, onPrimary)) { | ||||
|                 // This is kind of an impossible error: we're already modal | ||||
| @@ -1013,12 +1022,11 @@ var ScreenShield = class { | ||||
|                 mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|                 onComplete: () => { | ||||
|                     this._lockScreenShown({ fadeToBlack, animateFade: true }); | ||||
|                 } | ||||
|                 }, | ||||
|             }); | ||||
|         } else { | ||||
|             this._lockScreenGroup.fixed_position_set = false; | ||||
|             this._lockScreenShown({ fadeToBlack: fadeToBlack, | ||||
|                                     animateFade: false }); | ||||
|             this._lockScreenShown({ fadeToBlack, animateFade: false }); | ||||
|         } | ||||
|  | ||||
|         this._lockScreenGroup.grab_key_focus(); | ||||
| @@ -1036,9 +1044,10 @@ var ScreenShield = class { | ||||
|             this._animateArrows(); | ||||
|         } | ||||
|  | ||||
|         if (!this._arrowWatchId) | ||||
|         if (!this._arrowWatchId) { | ||||
|             this._arrowWatchId = this.idleMonitor.add_idle_watch(ARROW_IDLE_TIME, | ||||
|                                                                  this._pauseArrowAnimation.bind(this)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _pauseArrowAnimation() { | ||||
| @@ -1131,16 +1140,13 @@ var ScreenShield = class { | ||||
|                                                          vertical: true, | ||||
|                                                          style_class: 'screen-shield-contents-box' }); | ||||
|         this._clock = new Clock(); | ||||
|         this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true, | ||||
|                                                              y_fill: true }); | ||||
|         this._lockScreenContentsBox.add_child(this._clock); | ||||
|  | ||||
|         this._lockScreenContents.add_actor(this._lockScreenContentsBox); | ||||
|  | ||||
|         this._notificationsBox = new NotificationsBox(); | ||||
|         this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', this._wakeUpScreen.bind(this)); | ||||
|         this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true, | ||||
|                                                                         y_fill: true, | ||||
|                                                                         expand: true }); | ||||
|         this._lockScreenContentsBox.add_child(this._notificationsBox); | ||||
|  | ||||
|         this._hasLockScreen = true; | ||||
|     } | ||||
| @@ -1223,7 +1229,7 @@ var ScreenShield = class { | ||||
|             scale_y: 0, | ||||
|             duration: animate ? Overview.ANIMATION_TIME : 0, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this._completeDeactivate() | ||||
|             onComplete: () => this._completeDeactivate(), | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -1233,8 +1239,8 @@ var ScreenShield = class { | ||||
|             this._dialog = null; | ||||
|         } | ||||
|  | ||||
|         this._longLightbox.hide(); | ||||
|         this._shortLightbox.hide(); | ||||
|         this._longLightbox.lightOff(); | ||||
|         this._shortLightbox.lightOff(); | ||||
|         this.actor.hide(); | ||||
|  | ||||
|         if (this._becameActiveId != 0) { | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* exported ScreenshotService */ | ||||
|  | ||||
| const { Clutter, Gio, GLib, Meta, Shell, St } = imports.gi; | ||||
| const Signals = imports.signals; | ||||
| const { Clutter, Graphene, Gio, GObject, GLib, Meta, Shell, St } = imports.gi; | ||||
|  | ||||
| const GrabHelper = imports.ui.grabHelper; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| @@ -65,7 +64,55 @@ var ScreenshotService = class { | ||||
|                y + height <= global.screen_height; | ||||
|     } | ||||
|  | ||||
|     _onScreenshotComplete(result, area, filenameUsed, flash, invocation) { | ||||
|     *_resolveRelativeFilename(filename) { | ||||
|         if (GLib.str_has_suffix(filename, '.png')) | ||||
|             filename = filename.substr(0, -4); | ||||
|  | ||||
|         let path = [ | ||||
|             GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES), | ||||
|             GLib.get_home_dir(), | ||||
|         ].find(p => GLib.file_test(p, GLib.FileTest.EXISTS)); | ||||
|  | ||||
|         if (!path) | ||||
|             return null; | ||||
|  | ||||
|         yield Gio.File.new_for_path( | ||||
|             GLib.build_filenamev([path, `${filename}.png`])); | ||||
|  | ||||
|         for (let idx = 1; ; idx++) { | ||||
|             yield Gio.File.new_for_path( | ||||
|                 GLib.build_filenamev([path, `${filename}-${idx}.png`])); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _createStream(filename) { | ||||
|         if (filename == '') | ||||
|             return [Gio.MemoryOutputStream.new_resizable(), null]; | ||||
|  | ||||
|         if (GLib.path_is_absolute(filename)) { | ||||
|             try { | ||||
|                 let file = Gio.File.new_for_path(filename); | ||||
|                 let stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); | ||||
|                 return [stream, file]; | ||||
|             } catch (e) { | ||||
|                 return [null, null]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (let file of this._resolveRelativeFilename(filename)) { | ||||
|             try { | ||||
|                 let stream = file.create(Gio.FileCreateFlags.NONE, null); | ||||
|                 return [stream, file]; | ||||
|             } catch (e) { | ||||
|                 if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) | ||||
|                     return [null, null]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return [null, null]; | ||||
|     } | ||||
|  | ||||
|     _onScreenshotComplete(result, area, stream, file, flash, invocation) { | ||||
|         if (result) { | ||||
|             if (flash) { | ||||
|                 let flashspot = new Flashspot(area); | ||||
| @@ -77,6 +124,17 @@ var ScreenshotService = class { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         stream.close(null); | ||||
|  | ||||
|         let filenameUsed = ''; | ||||
|         if (file) { | ||||
|             filenameUsed = file.get_path(); | ||||
|         } else { | ||||
|             let bytes = stream.steal_as_bytes(); | ||||
|             let clipboard = St.Clipboard.get_default(); | ||||
|             clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes); | ||||
|         } | ||||
|  | ||||
|         let retval = GLib.Variant.new('(bs)', [result, filenameUsed]); | ||||
|         invocation.return_value(retval); | ||||
|     } | ||||
| @@ -111,15 +169,18 @@ var ScreenshotService = class { | ||||
|         let screenshot = this._createScreenshot(invocation); | ||||
|         if (!screenshot) | ||||
|             return; | ||||
|         screenshot.screenshot_area (x, y, width, height, filename, | ||||
|  | ||||
|         let [stream, file] = this._createStream(filename); | ||||
|  | ||||
|         screenshot.screenshot_area(x, y, width, height, stream, | ||||
|             (o, res) => { | ||||
|                 try { | ||||
|                     let [result, area, filenameUsed] = | ||||
|                     let [result, area] = | ||||
|                         screenshot.screenshot_area_finish(res); | ||||
|                     this._onScreenshotComplete( | ||||
|                         result, area, filenameUsed, flash, invocation); | ||||
|                         result, area, stream, file, flash, invocation); | ||||
|                 } catch (e) { | ||||
|                     invocation.return_gerror (e); | ||||
|                     invocation.return_gerror(e); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
| @@ -129,15 +190,18 @@ var ScreenshotService = class { | ||||
|         let screenshot = this._createScreenshot(invocation); | ||||
|         if (!screenshot) | ||||
|             return; | ||||
|         screenshot.screenshot_window (includeFrame, includeCursor, filename, | ||||
|  | ||||
|         let [stream, file] = this._createStream(filename); | ||||
|  | ||||
|         screenshot.screenshot_window(includeFrame, includeCursor, stream, | ||||
|             (o, res) => { | ||||
|                 try { | ||||
|                     let [result, area, filenameUsed] = | ||||
|                     let [result, area] = | ||||
|                         screenshot.screenshot_window_finish(res); | ||||
|                     this._onScreenshotComplete( | ||||
|                         result, area, filenameUsed, flash, invocation); | ||||
|                         result, area, stream, file, flash, invocation); | ||||
|                 } catch (e) { | ||||
|                     invocation.return_gerror (e); | ||||
|                     invocation.return_gerror(e); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
| @@ -147,15 +211,18 @@ var ScreenshotService = class { | ||||
|         let screenshot = this._createScreenshot(invocation); | ||||
|         if (!screenshot) | ||||
|             return; | ||||
|         screenshot.screenshot(includeCursor, filename, | ||||
|  | ||||
|         let [stream, file] = this._createStream(filename); | ||||
|  | ||||
|         screenshot.screenshot(includeCursor, stream, | ||||
|             (o, res) => { | ||||
|                 try { | ||||
|                     let [result, area, filenameUsed] = | ||||
|                     let [result, area] = | ||||
|                         screenshot.screenshot_finish(res); | ||||
|                     this._onScreenshotComplete( | ||||
|                         result, area, filenameUsed, flash, invocation); | ||||
|                         result, area, stream, file, flash, invocation); | ||||
|                 } catch (e) { | ||||
|                     invocation.return_gerror (e); | ||||
|                     invocation.return_gerror(e); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
| @@ -163,7 +230,7 @@ var ScreenshotService = class { | ||||
|     SelectAreaAsync(params, invocation) { | ||||
|         let selectArea = new SelectArea(); | ||||
|         selectArea.show(); | ||||
|         selectArea.connect('finished', (selectArea, areaRectangle) => { | ||||
|         selectArea.connect('finished', (o, areaRectangle) => { | ||||
|             if (areaRectangle) { | ||||
|                 let retRectangle = this._unscaleArea(areaRectangle.x, areaRectangle.y, | ||||
|                                                      areaRectangle.width, areaRectangle.height); | ||||
| @@ -185,7 +252,7 @@ var ScreenshotService = class { | ||||
|                                             "Invalid params"); | ||||
|             return; | ||||
|         } | ||||
|         let flashspot = new Flashspot({ x: x, y: y, width: width, height: height }); | ||||
|         let flashspot = new Flashspot({ x, y, width, height }); | ||||
|         flashspot.fire(); | ||||
|         invocation.return_value(null); | ||||
|     } | ||||
| @@ -193,20 +260,20 @@ var ScreenshotService = class { | ||||
|     PickColorAsync(params, invocation) { | ||||
|         let pickPixel = new PickPixel(); | ||||
|         pickPixel.show(); | ||||
|         pickPixel.connect('finished', (pickPixel, coords) => { | ||||
|         pickPixel.connect('finished', (obj, coords) => { | ||||
|             if (coords) { | ||||
|                 let screenshot = this._createScreenshot(invocation, false); | ||||
|                 if (!screenshot) | ||||
|                     return; | ||||
|                 screenshot.pick_color(...coords, (o, res) => { | ||||
|                 screenshot.pick_color(coords.x, coords.y, (_o, res) => { | ||||
|                     let [success_, color] = screenshot.pick_color_finish(res); | ||||
|                     let { red, green, blue } = color; | ||||
|                     let retval = GLib.Variant.new('(a{sv})', [{ | ||||
|                         color: GLib.Variant.new('(ddd)', [ | ||||
|                             red / 255.0, | ||||
|                             green / 255.0, | ||||
|                             blue / 255.0 | ||||
|                         ]) | ||||
|                             blue / 255.0, | ||||
|                         ]), | ||||
|                     }]); | ||||
|                     this._removeShooterForSender(invocation.get_sender()); | ||||
|                     invocation.return_value(retval); | ||||
| @@ -219,62 +286,61 @@ var ScreenshotService = class { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| var SelectArea = class { | ||||
|     constructor() { | ||||
| var SelectArea = GObject.registerClass({ | ||||
|     Signals: { 'finished': { param_types: [Meta.Rectangle.$gtype] } }, | ||||
| }, class SelectArea extends St.Widget { | ||||
|     _init() { | ||||
|         this._startX = -1; | ||||
|         this._startY = -1; | ||||
|         this._lastX = 0; | ||||
|         this._lastY = 0; | ||||
|         this._result = null; | ||||
|  | ||||
|         this._group = new St.Widget({ visible: false, | ||||
|                                       reactive: true, | ||||
|                                       x: 0, | ||||
|                                       y: 0 }); | ||||
|         Main.uiGroup.add_actor(this._group); | ||||
|         super._init({ | ||||
|             visible: false, | ||||
|             reactive: true, | ||||
|             x: 0, | ||||
|             y: 0, | ||||
|         }); | ||||
|         Main.uiGroup.add_actor(this); | ||||
|  | ||||
|         this._grabHelper = new GrabHelper.GrabHelper(this._group); | ||||
|  | ||||
|         this._group.connect('button-press-event', | ||||
|                             this._onButtonPress.bind(this)); | ||||
|         this._group.connect('button-release-event', | ||||
|                             this._onButtonRelease.bind(this)); | ||||
|         this._group.connect('motion-event', | ||||
|                             this._onMotionEvent.bind(this)); | ||||
|         this._grabHelper = new GrabHelper.GrabHelper(this); | ||||
|  | ||||
|         let constraint = new Clutter.BindConstraint({ source: global.stage, | ||||
|                                                       coordinate: Clutter.BindCoordinate.ALL }); | ||||
|         this._group.add_constraint(constraint); | ||||
|         this.add_constraint(constraint); | ||||
|  | ||||
|         this._rubberband = new St.Widget({ | ||||
|             style_class: 'select-area-rubberband', | ||||
|             visible: false | ||||
|             visible: false, | ||||
|         }); | ||||
|         this._group.add_actor(this._rubberband); | ||||
|         this.add_actor(this._rubberband); | ||||
|     } | ||||
|  | ||||
|     show() { | ||||
|         if (!this._grabHelper.grab({ actor: this._group, | ||||
|     vfunc_show() { | ||||
|         if (!this._grabHelper.grab({ actor: this, | ||||
|                                      onUngrab: this._onUngrab.bind(this) })) | ||||
|             return; | ||||
|  | ||||
|         global.display.set_cursor(Meta.Cursor.CROSSHAIR); | ||||
|         Main.uiGroup.set_child_above_sibling(this._group, null); | ||||
|         this._group.visible = true; | ||||
|         Main.uiGroup.set_child_above_sibling(this, null); | ||||
|         super.vfunc_show(); | ||||
|     } | ||||
|  | ||||
|     _getGeometry() { | ||||
|         return { x: Math.min(this._startX, this._lastX), | ||||
|                  y: Math.min(this._startY, this._lastY), | ||||
|                  width: Math.abs(this._startX - this._lastX) + 1, | ||||
|                  height: Math.abs(this._startY - this._lastY) + 1 }; | ||||
|         return new Meta.Rectangle({ | ||||
|             x: Math.min(this._startX, this._lastX), | ||||
|             y: Math.min(this._startY, this._lastY), | ||||
|             width: Math.abs(this._startX - this._lastX) + 1, | ||||
|             height: Math.abs(this._startY - this._lastY) + 1, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _onMotionEvent(actor, event) { | ||||
|     vfunc_motion_event(motionEvent) { | ||||
|         if (this._startX == -1 || this._startY == -1 || this._result) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         [this._lastX, this._lastY] = event.get_coords(); | ||||
|         [this._lastX, this._lastY] = [motionEvent.x, motionEvent.y]; | ||||
|         this._lastX = Math.floor(this._lastX); | ||||
|         this._lastY = Math.floor(this._lastY); | ||||
|         let geometry = this._getGeometry(); | ||||
| @@ -286,8 +352,8 @@ var SelectArea = class { | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onButtonPress(actor, event) { | ||||
|         [this._startX, this._startY] = event.get_coords(); | ||||
|     vfunc_button_press_event(buttonEvent) { | ||||
|         [this._startX, this._startY] = [buttonEvent.x, buttonEvent.y]; | ||||
|         this._startX = Math.floor(this._startX); | ||||
|         this._startY = Math.floor(this._startY); | ||||
|         this._rubberband.set_position(this._startX, this._startY); | ||||
| @@ -295,13 +361,13 @@ var SelectArea = class { | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
|  | ||||
|     _onButtonRelease() { | ||||
|     vfunc_button_release_event() { | ||||
|         this._result = this._getGeometry(); | ||||
|         this._group.ease({ | ||||
|         this.ease({ | ||||
|             opacity: 0, | ||||
|             duration: 200, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
|             onComplete: () => this._grabHelper.ungrab() | ||||
|             onComplete: () => this._grabHelper.ungrab(), | ||||
|         }); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| @@ -311,43 +377,42 @@ var SelectArea = class { | ||||
|         this.emit('finished', this._result); | ||||
|  | ||||
|         GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { | ||||
|             this._group.destroy(); | ||||
|             this.destroy(); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(SelectArea.prototype); | ||||
| }); | ||||
|  | ||||
| var PickPixel = GObject.registerClass({ | ||||
|     Signals: { 'finished': { param_types: [Graphene.Point.$gtype] } }, | ||||
| }, class PickPixel extends St.Widget { | ||||
|     _init() { | ||||
|         super._init({ visible: false, reactive: true }); | ||||
|  | ||||
| var PickPixel = class { | ||||
|     constructor() { | ||||
|         this._result = null; | ||||
|  | ||||
|         this._group = new St.Widget({ visible: false, | ||||
|                                       reactive: true }); | ||||
|         Main.uiGroup.add_actor(this._group); | ||||
|         Main.uiGroup.add_actor(this); | ||||
|  | ||||
|         this._grabHelper = new GrabHelper.GrabHelper(this._group); | ||||
|  | ||||
|         this._group.connect('button-release-event', | ||||
|                             this._onButtonRelease.bind(this)); | ||||
|         this._grabHelper = new GrabHelper.GrabHelper(this); | ||||
|  | ||||
|         let constraint = new Clutter.BindConstraint({ source: global.stage, | ||||
|                                                       coordinate: Clutter.BindCoordinate.ALL }); | ||||
|         this._group.add_constraint(constraint); | ||||
|         this.add_constraint(constraint); | ||||
|     } | ||||
|  | ||||
|     show() { | ||||
|         if (!this._grabHelper.grab({ actor: this._group, | ||||
|     vfunc_show() { | ||||
|         if (!this._grabHelper.grab({ actor: this, | ||||
|                                      onUngrab: this._onUngrab.bind(this) })) | ||||
|             return; | ||||
|  | ||||
|         global.display.set_cursor(Meta.Cursor.CROSSHAIR); | ||||
|         Main.uiGroup.set_child_above_sibling(this._group, null); | ||||
|         this._group.visible = true; | ||||
|         Main.uiGroup.set_child_above_sibling(this, null); | ||||
|         super.vfunc_show(); | ||||
|     } | ||||
|  | ||||
|     _onButtonRelease(actor, event) { | ||||
|         this._result = event.get_coords(); | ||||
|     vfunc_button_release_event(buttonEvent) { | ||||
|         let { x, y } = buttonEvent; | ||||
|         this._result = new Graphene.Point({ x, y }); | ||||
|         this._grabHelper.ungrab(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| @@ -357,29 +422,29 @@ var PickPixel = class { | ||||
|         this.emit('finished', this._result); | ||||
|  | ||||
|         GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { | ||||
|             this._group.destroy(); | ||||
|             this.destroy(); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| Signals.addSignalMethods(PickPixel.prototype); | ||||
| }); | ||||
|  | ||||
| var FLASHSPOT_ANIMATION_OUT_TIME = 500; // milliseconds | ||||
|  | ||||
| var Flashspot = class extends Lightbox.Lightbox { | ||||
|     constructor(area) { | ||||
|         super(Main.uiGroup, { inhibitEvents: true, | ||||
|                               width: area.width, | ||||
|                               height: area.height }); | ||||
|  | ||||
|         this.actor.style_class = 'flashspot'; | ||||
|         this.actor.set_position(area.x, area.y); | ||||
| var Flashspot = GObject.registerClass( | ||||
| class Flashspot extends Lightbox.Lightbox { | ||||
|     _init(area) { | ||||
|         super._init(Main.uiGroup, { | ||||
|             inhibitEvents: true, | ||||
|             width: area.width, | ||||
|             height: area.height, | ||||
|         }); | ||||
|         this.style_class = 'flashspot'; | ||||
|         this.set_position(area.x, area.y); | ||||
|     } | ||||
|  | ||||
|     fire(doneCallback) { | ||||
|         this.actor.show(); | ||||
|         this.actor.opacity = 255; | ||||
|         this.actor.ease({ | ||||
|         this.set({ visible: true, opacity: 255 }); | ||||
|         this.ease({ | ||||
|             opacity: 0, | ||||
|             duration: FLASHSPOT_ANIMATION_OUT_TIME, | ||||
|             mode: Clutter.AnimationMode.EASE_OUT_QUAD, | ||||
| @@ -387,7 +452,7 @@ var Flashspot = class extends Lightbox.Lightbox { | ||||
|                 if (doneCallback) | ||||
|                     doneCallback(); | ||||
|                 this.destroy(); | ||||
|             } | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| }); | ||||
|   | ||||
| @@ -32,7 +32,8 @@ const { loadInterfaceXML } = imports.misc.fileUtils; | ||||
|  | ||||
| /** | ||||
|  * sleep: | ||||
|  * @milliseconds: number of milliseconds to wait | ||||
|  * @param {number} milliseconds - number of milliseconds to wait | ||||
|  * @returns {Promise} that resolves after @milliseconds ms | ||||
|  * | ||||
|  * Used within an automation script to pause the the execution of the | ||||
|  * current script for the specified amount of time. Use as | ||||
| @@ -50,6 +51,7 @@ function sleep(milliseconds) { | ||||
|  | ||||
| /** | ||||
|  * waitLeisure: | ||||
|  * @returns {Promise} that resolves when the shell is idle | ||||
|  * | ||||
|  * Used within an automation script to pause the the execution of the | ||||
|  * current script until the shell is completely idle. Use as | ||||
| @@ -90,13 +92,13 @@ function _callRemote(obj, method, ...args) { | ||||
|  | ||||
| /** | ||||
|  * createTestWindow: | ||||
|  * @params: options for window creation. | ||||
|  *   width - width of window, in pixels (default 640) | ||||
|  *   height - height of window, in pixels (default 480) | ||||
|  *   alpha - whether the window should have an alpha channel (default false) | ||||
|  *   maximized - whether the window should be created maximized (default false) | ||||
|  *   redraws - whether the window should continually redraw itself (default false) | ||||
|  * @maximized: whethe the window should be created maximized | ||||
|  * @param {Object} params: options for window creation. | ||||
|  *   {number} [params.width=640] - width of window, in pixels | ||||
|  *   {number} [params.height=480] - height of window, in pixels | ||||
|  *   {bool} [params.alpha=false] - whether the window should have an alpha channel | ||||
|  *   {bool} [params.maximized=false] - whether the window should be created maximized | ||||
|  *   {bool} [params.redraws=false] - whether the window should continually redraw itself | ||||
|  * @returns {Promise} | ||||
|  * | ||||
|  * Creates a window using gnome-shell-perf-helper for testing purposes. | ||||
|  * While this function can be used with yield in an automation | ||||
| @@ -119,6 +121,7 @@ function createTestWindow(params) { | ||||
|  | ||||
| /** | ||||
|  * waitTestWindows: | ||||
|  * @returns {Promise} | ||||
|  * | ||||
|  * Used within an automation script to pause until all windows previously | ||||
|  * created with createTestWindow have been mapped and exposed. | ||||
| @@ -130,6 +133,7 @@ function waitTestWindows() { | ||||
|  | ||||
| /** | ||||
|  * destroyTestWindows: | ||||
|  * @returns {Promise} | ||||
|  * | ||||
|  * Destroys all windows previously created with createTestWindow(). | ||||
|  * While this function can be used with yield in an automation | ||||
| @@ -144,8 +148,8 @@ function destroyTestWindows() { | ||||
|  | ||||
| /** | ||||
|  * defineScriptEvent | ||||
|  * @name: The event will be called script.<name> | ||||
|  * @description: Short human-readable description of the event | ||||
|  * @param {string} name: The event will be called script.<name> | ||||
|  * @param {string} description: Short human-readable description of the event | ||||
|  * | ||||
|  * Convenience function to define a zero-argument performance event | ||||
|  * within the 'script' namespace that is reserved for events defined locally | ||||
| @@ -159,7 +163,7 @@ function defineScriptEvent(name, description) { | ||||
|  | ||||
| /** | ||||
|  * scriptEvent | ||||
|  * @name: Name registered with defineScriptEvent() | ||||
|  * @param {string} name: Name registered with defineScriptEvent() | ||||
|  * | ||||
|  * Convenience function to record a script-local performance event | ||||
|  * previously defined with defineScriptEvent | ||||
| @@ -200,8 +204,8 @@ function _collect(scriptModule, outputFile) { | ||||
|         let raw = f.replace(null, false, | ||||
|                             Gio.FileCreateFlags.NONE, | ||||
|                             null); | ||||
|         let out = Gio.BufferedOutputStream.new_sized (raw, 4096); | ||||
|         Shell.write_string_to_stream (out, "{\n"); | ||||
|         let out = Gio.BufferedOutputStream.new_sized(raw, 4096); | ||||
|         Shell.write_string_to_stream(out, "{\n"); | ||||
|  | ||||
|         Shell.write_string_to_stream(out, '"events":\n'); | ||||
|         Shell.PerfLog.get_default().dump_events(out); | ||||
| @@ -250,10 +254,10 @@ function _collect(scriptModule, outputFile) { | ||||
|         } | ||||
|         Shell.write_string_to_stream(out, ' ]'); | ||||
|  | ||||
|         Shell.write_string_to_stream (out, ',\n"log":\n'); | ||||
|         Shell.write_string_to_stream(out, ',\n"log":\n'); | ||||
|         Shell.PerfLog.get_default().dump_log(out); | ||||
|  | ||||
|         Shell.write_string_to_stream (out, '\n}\n'); | ||||
|         Shell.write_string_to_stream(out, '\n}\n'); | ||||
|         out.close(null); | ||||
|     } else { | ||||
|         let metrics = []; | ||||
| @@ -262,20 +266,21 @@ function _collect(scriptModule, outputFile) { | ||||
|  | ||||
|         metrics.sort(); | ||||
|  | ||||
|         print ('------------------------------------------------------------'); | ||||
|         print('------------------------------------------------------------'); | ||||
|         for (let i = 0; i < metrics.length; i++) { | ||||
|             let metric = metrics[i]; | ||||
|             print (`# ${scriptModule.METRICS[metric].description}`); | ||||
|             print (`${metric}: ${scriptModule.METRICS[metric].value}${scriptModule.METRICS[metric].units}`); | ||||
|             print(`# ${scriptModule.METRICS[metric].description}`); | ||||
|             print(`${metric}: ${scriptModule.METRICS[metric].value}${scriptModule.METRICS[metric].units}`); | ||||
|         } | ||||
|         print ('------------------------------------------------------------'); | ||||
|         print('------------------------------------------------------------'); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * runPerfScript | ||||
|  * @scriptModule: module object with run and finish functions | ||||
|  *    and event handlers | ||||
|  * @param {Object} scriptModule: module object with run and finish | ||||
|  *    functions and event handlers | ||||
|  * @param {string} outputFile: path to write output to | ||||
|  * | ||||
|  * Runs a script for automated collection of performance data. The | ||||
|  * script is defined as a Javascript module with specified contents. | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user