Merge lp:~mterry/unity-greeter/animation-fixes into lp:unity-greeter
- animation-fixes
- Merge into trunk
Proposed by
Michael Terry
Status: | Merged |
---|---|
Merged at revision: | 352 |
Proposed branch: | lp:~mterry/unity-greeter/animation-fixes |
Merge into: | lp:unity-greeter |
Prerequisite: | lp:~mterry/unity-greeter/responsiveness-fixes |
Diff against target: |
612 lines (+181/-180) 9 files modified
src/Makefile.am (+1/-0) src/animate-timer.vala (+30/-119) src/background.vala (+2/-1) src/cached-image.vala (+56/-0) src/dash-button.vala (+12/-3) src/fadable.vala (+1/-1) src/main-window.vala (+6/-5) src/session-chooser.vala (+1/-1) src/user-list.vala (+72/-50) |
To merge this branch: | bzr merge lp:~mterry/unity-greeter/animation-fixes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ken VanDine | Approve | ||
Unity Greeter Development Team | Pending | ||
Review via email: mp+97233@code.launchpad.net |
Commit message
Description of the change
In conjunction with design, some further easing tweaks (different algorithm for scrolling and different duration times).
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/Makefile.am' | |||
2 | --- src/Makefile.am 2012-02-21 17:31:59 +0000 | |||
3 | +++ src/Makefile.am 2012-03-13 14:51:31 +0000 | |||
4 | @@ -8,6 +8,7 @@ | |||
5 | 8 | indicator.vapi \ | 8 | indicator.vapi \ |
6 | 9 | animate-timer.vala \ | 9 | animate-timer.vala \ |
7 | 10 | background.vala \ | 10 | background.vala \ |
8 | 11 | cached-image.vala \ | ||
9 | 11 | dash-box.vala \ | 12 | dash-box.vala \ |
10 | 12 | dash-button.vala \ | 13 | dash-button.vala \ |
11 | 13 | dash-entry.vala \ | 14 | dash-entry.vala \ |
12 | 14 | 15 | ||
13 | === modified file 'src/animate-timer.vala' | |||
14 | --- src/animate-timer.vala 2012-02-21 17:31:59 +0000 | |||
15 | +++ src/animate-timer.vala 2012-03-13 14:51:31 +0000 | |||
16 | @@ -20,6 +20,9 @@ | |||
17 | 20 | 20 | ||
18 | 21 | private class AnimateTimer : Object | 21 | private class AnimateTimer : Object |
19 | 22 | { | 22 | { |
20 | 23 | /* x and y are 0.0 to 1.0 */ | ||
21 | 24 | public delegate double EasingFunc (double x); | ||
22 | 25 | |||
23 | 23 | /* The following are the same intervals that Unity uses */ | 26 | /* The following are the same intervals that Unity uses */ |
24 | 24 | public static const int INSTANT = 150; /* Good for animations that don't convey any information */ | 27 | public static const int INSTANT = 150; /* Good for animations that don't convey any information */ |
25 | 25 | public static const int FAST = 250; /* Good for animations that convey duplicated information */ | 28 | public static const int FAST = 250; /* Good for animations that convey duplicated information */ |
26 | @@ -27,6 +30,7 @@ | |||
27 | 27 | public static const int SLOW = 1000; /* Good for animations that convey information that is only presented in the animation */ | 30 | public static const int SLOW = 1000; /* Good for animations that convey information that is only presented in the animation */ |
28 | 28 | 31 | ||
29 | 29 | /* speed is in milliseconds */ | 32 | /* speed is in milliseconds */ |
30 | 33 | public unowned EasingFunc easing_func {get; private set;} | ||
31 | 30 | public int speed {get; set;} | 34 | public int speed {get; set;} |
32 | 31 | public bool is_running { get { return timeout != 0; } } | 35 | public bool is_running { get { return timeout != 0; } } |
33 | 32 | public double progress {get; private set;} | 36 | public double progress {get; private set;} |
34 | @@ -35,9 +39,10 @@ | |||
35 | 35 | public signal void animate (double progress); | 39 | public signal void animate (double progress); |
36 | 36 | 40 | ||
37 | 37 | /* speed is in milliseconds */ | 41 | /* speed is in milliseconds */ |
39 | 38 | public AnimateTimer (int speed) | 42 | public AnimateTimer (EasingFunc func, int speed) |
40 | 39 | { | 43 | { |
41 | 40 | Object (speed: speed); | 44 | Object (speed: speed); |
42 | 45 | this.easing_func = func; | ||
43 | 41 | } | 46 | } |
44 | 42 | 47 | ||
45 | 43 | /* temp_speed is in milliseconds */ | 48 | /* temp_speed is in milliseconds */ |
46 | @@ -57,38 +62,6 @@ | |||
47 | 57 | length = temp_speed * TimeSpan.MILLISECOND; | 62 | length = temp_speed * TimeSpan.MILLISECOND; |
48 | 58 | } | 63 | } |
49 | 59 | 64 | ||
50 | 60 | /* This tells us the consumer of our progress reports is changing the | ||
51 | 61 | goalposts on us. So we need to adjust our reports accordingly. | ||
52 | 62 | 'extra_progress' is how much progress is being added to the animation. | ||
53 | 63 | */ | ||
54 | 64 | public bool extend (double new_progress) | ||
55 | 65 | { | ||
56 | 66 | /* As an example, say a consumer is animating from point A to point C in | ||
57 | 67 | 150ms. This is two points to cover, and at 75ms, they will be 0.5 done. | ||
58 | 68 | |||
59 | 69 | Now, they extend it to point D. This adds an extra point, which is half | ||
60 | 70 | the original progress. So now they call extend (0.5). | ||
61 | 71 | |||
62 | 72 | We extend in the middle, so we need the slope at halfway through our | ||
63 | 73 | progress function. That is a constant we have (see calculate_progress | ||
64 | 74 | for details). So we'd need to add the same amount of normalized time | ||
65 | 75 | to our process as progress, which is 0.5 / HALFWAY_VELOCITY = 0.318 in | ||
66 | 76 | this example. Which is (length * 0.318) = 47.7ms more time to add. | ||
67 | 77 | |||
68 | 78 | Now we have 197.7ms as length of our animation. Which changes the | ||
69 | 79 | progress values we give out. We further need to adjust our progress | ||
70 | 80 | function to take this 'pause' in the middle of the function into | ||
71 | 81 | account. | ||
72 | 82 | */ | ||
73 | 83 | |||
74 | 84 | if (progress > HALFWAY_PROGRESS) | ||
75 | 85 | return false; | ||
76 | 86 | |||
77 | 87 | extra_progress += new_progress; | ||
78 | 88 | extra_time = (TimeSpan)(length * extra_progress / HALFWAY_VELOCITY); | ||
79 | 89 | return true; | ||
80 | 90 | } | ||
81 | 91 | |||
82 | 92 | public void stop () | 65 | public void stop () |
83 | 93 | { | 66 | { |
84 | 94 | if (timeout != 0) | 67 | if (timeout != 0) |
85 | @@ -102,13 +75,6 @@ | |||
86 | 102 | private TimeSpan extra_time = 0; | 75 | private TimeSpan extra_time = 0; |
87 | 103 | private double extra_progress = 0.0; | 76 | private double extra_progress = 0.0; |
88 | 104 | 77 | ||
89 | 105 | /* Derivative of our easing function at 0.5 (the halfway point). | ||
90 | 106 | See calculate_progress() for functions. */ | ||
91 | 107 | private static const double HALFWAY_VELOCITY = 1.5708; | ||
92 | 108 | |||
93 | 109 | /* The y value of our easing function at 0.5 (the halway point) */ | ||
94 | 110 | private static const double HALFWAY_PROGRESS = 0.75; | ||
95 | 111 | |||
96 | 112 | private bool animate_cb () | 78 | private bool animate_cb () |
97 | 113 | { | 79 | { |
98 | 114 | if (start_time == 0) | 80 | if (start_time == 0) |
99 | @@ -129,94 +95,39 @@ | |||
100 | 129 | /* Returns 0.0 to 1.0 where 1.0 is at or past end_time */ | 95 | /* Returns 0.0 to 1.0 where 1.0 is at or past end_time */ |
101 | 130 | private double normalize_time (TimeSpan now) | 96 | private double normalize_time (TimeSpan now) |
102 | 131 | { | 97 | { |
127 | 132 | var total = length; | 98 | return (((double)(now - start_time)) / length).clamp (0.0, 1.0); |
104 | 133 | var halfway = start_time + length / 2; | ||
105 | 134 | if (now > halfway) | ||
106 | 135 | now -= extra_time; | ||
107 | 136 | |||
108 | 137 | return (((double)(now - start_time)) / total).clamp (0.0, 1.0); | ||
109 | 138 | } | ||
110 | 139 | |||
111 | 140 | /* Incoming progress count is original, un-extra-time enhanced progress */ | ||
112 | 141 | private double normalize_progress (double p) | ||
113 | 142 | { | ||
114 | 143 | /* So without extra time, progress goes from 0.0 to 1.0. But with | ||
115 | 144 | extra time, the beginning and end want to keep the same function, | ||
116 | 145 | just squeezed into a smaller place in the graph. So we figure out | ||
117 | 146 | that mapping here. */ | ||
118 | 147 | var extra_mapped = extra_progress / (1.0 + extra_progress); | ||
119 | 148 | var half_mapped = HALFWAY_PROGRESS / (1.0 + extra_progress); | ||
120 | 149 | if (p < HALFWAY_PROGRESS) | ||
121 | 150 | return p * (half_mapped / HALFWAY_PROGRESS); | ||
122 | 151 | else | ||
123 | 152 | { | ||
124 | 153 | var end = 1.0 - half_mapped - extra_mapped; | ||
125 | 154 | return (p - HALFWAY_PROGRESS) * (end / (1 - HALFWAY_PROGRESS)) + half_mapped + extra_mapped; | ||
126 | 155 | } | ||
128 | 156 | } | 99 | } |
129 | 157 | 100 | ||
130 | 158 | /* Returns 0.0 to 1.0 where 1.0 is done. | 101 | /* Returns 0.0 to 1.0 where 1.0 is done. |
131 | 159 | time is not normalized yet! */ | 102 | time is not normalized yet! */ |
132 | 160 | private double calculate_progress (TimeSpan time) | 103 | private double calculate_progress (TimeSpan time) |
133 | 161 | { | 104 | { |
183 | 162 | /* Use a sine wave function similar to what Unity uses. They call it | 105 | var x = normalize_time (time); |
184 | 163 | 'easing' and is designed to make the animation start and end slower | 106 | var y = easing_func (x); |
136 | 164 | than in the middle. | ||
137 | 165 | |||
138 | 166 | ((1 - Math.cos (Math.PI * time)) / 2) is the basic sine curve for | ||
139 | 167 | easing, used by Unity and others. By squaring that and reversing | ||
140 | 168 | the curve (by using 1 - x and 1 - y), we get a more exaggerated | ||
141 | 169 | slowdown. | ||
142 | 170 | |||
143 | 171 | For clarity, here is the whole function: | ||
144 | 172 | y = 1 - ((1 - cos (pi * (1 - x))) / 2) ^ 2 | ||
145 | 173 | |||
146 | 174 | Here is the derivative of that function (useful for calculating | ||
147 | 175 | slope at a given point, used elsewhere in this class): | ||
148 | 176 | y = (pi * (cos (pi * (x - 1)) - 1)) * sin (pi * (x - 1)) / 2 | ||
149 | 177 | |||
150 | 178 | */ | ||
151 | 179 | |||
152 | 180 | /* But due to the ability to add extra time into the equation, we | ||
153 | 181 | are actually three functions: | ||
154 | 182 | 1) x=[0,A) = normal | ||
155 | 183 | 2) x=[A,B] = halfway slope | ||
156 | 184 | 3) x=(B,1.0] = picks up where first function left off */ | ||
157 | 185 | |||
158 | 186 | var y = 0.0; | ||
159 | 187 | var orig_half_point = start_time + length / 2; | ||
160 | 188 | |||
161 | 189 | if (time > start_time + length + extra_time) | ||
162 | 190 | { | ||
163 | 191 | y = 1.0; | ||
164 | 192 | } | ||
165 | 193 | else if (time < orig_half_point) | ||
166 | 194 | { | ||
167 | 195 | var x = normalize_time (time); | ||
168 | 196 | y = easing_function (x); | ||
169 | 197 | y = normalize_progress (y); | ||
170 | 198 | } | ||
171 | 199 | else if (time < orig_half_point + extra_time) | ||
172 | 200 | { | ||
173 | 201 | var xpercent = (time - orig_half_point) / (double)extra_time; | ||
174 | 202 | y = (xpercent * extra_progress + HALFWAY_PROGRESS) / (1.0 + extra_progress); | ||
175 | 203 | } | ||
176 | 204 | else | ||
177 | 205 | { | ||
178 | 206 | var x = normalize_time (time); | ||
179 | 207 | y = easing_function (x); | ||
180 | 208 | y = normalize_progress (y); | ||
181 | 209 | } | ||
182 | 210 | |||
185 | 211 | return y.clamp (0.0, 1.0); | 107 | return y.clamp (0.0, 1.0); |
186 | 212 | } | 108 | } |
187 | 213 | 109 | ||
194 | 214 | private double easing_function (double x) | 110 | public static double ease_in_out (double x) |
195 | 215 | { | 111 | { |
196 | 216 | var y = (1 - Math.cos (Math.PI * (1.0 - x))) / 2; | 112 | return (1 - Math.cos (Math.PI * x)) / 2; |
197 | 217 | y = y * y; | 113 | } |
198 | 218 | y = 1.0 - y; | 114 | |
199 | 219 | return y; | 115 | /*public static double ease_in_quad (double x) |
200 | 116 | { | ||
201 | 117 | return Math.pow (x, 2); | ||
202 | 118 | }*/ | ||
203 | 119 | /*public static double ease_out_quad (double x) | ||
204 | 120 | { | ||
205 | 121 | return -1 * Math.pow (x - 1, 2) + 1; | ||
206 | 122 | }*/ | ||
207 | 123 | |||
208 | 124 | /*public static double ease_in_quint (double x) | ||
209 | 125 | { | ||
210 | 126 | return Math.pow (x, 5); | ||
211 | 127 | }*/ | ||
212 | 128 | public static double ease_out_quint (double x) | ||
213 | 129 | { | ||
214 | 130 | return Math.pow (x - 1, 5) + 1; | ||
215 | 220 | } | 131 | } |
216 | 221 | } | 132 | } |
217 | 222 | 133 | ||
218 | 223 | 134 | ||
219 | === modified file 'src/background.vala' | |||
220 | --- src/background.vala 2012-02-21 22:36:02 +0000 | |||
221 | +++ src/background.vala 2012-03-13 14:51:31 +0000 | |||
222 | @@ -247,7 +247,7 @@ | |||
223 | 247 | public Background (Cairo.Surface target_surface) | 247 | public Background (Cairo.Surface target_surface) |
224 | 248 | { | 248 | { |
225 | 249 | this.target_surface = target_surface; | 249 | this.target_surface = target_surface; |
227 | 250 | timer = new AnimateTimer (AnimateTimer.INSTANT); | 250 | timer = new AnimateTimer (AnimateTimer.ease_in_out, 700); |
228 | 251 | timer.animate.connect (animate_cb); | 251 | timer.animate.connect (animate_cb); |
229 | 252 | 252 | ||
230 | 253 | loaders = new HashTable<string?, BackgroundLoader> (str_hash, str_equal); | 253 | loaders = new HashTable<string?, BackgroundLoader> (str_hash, str_equal); |
231 | @@ -426,6 +426,7 @@ | |||
232 | 426 | { | 426 | { |
233 | 427 | old = current; | 427 | old = current; |
234 | 428 | current = new_background; | 428 | current = new_background; |
235 | 429 | timer.stop (); | ||
236 | 429 | } | 430 | } |
237 | 430 | 431 | ||
238 | 431 | queue_draw (); | 432 | queue_draw (); |
239 | 432 | 433 | ||
240 | === added file 'src/cached-image.vala' | |||
241 | --- src/cached-image.vala 1970-01-01 00:00:00 +0000 | |||
242 | +++ src/cached-image.vala 2012-03-13 14:51:31 +0000 | |||
243 | @@ -0,0 +1,56 @@ | |||
244 | 1 | /* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 4 -*- | ||
245 | 2 | * | ||
246 | 3 | * Copyright (C) 2012 Canonical Ltd | ||
247 | 4 | * | ||
248 | 5 | * This program is free software: you can redistribute it and/or modify | ||
249 | 6 | * it under the terms of the GNU General Public License version 3 as | ||
250 | 7 | * published by the Free Software Foundation. | ||
251 | 8 | * | ||
252 | 9 | * This program is distributed in the hope that it will be useful, | ||
253 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
254 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
255 | 12 | * GNU General Public License for more details. | ||
256 | 13 | * | ||
257 | 14 | * You should have received a copy of the GNU General Public License | ||
258 | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
259 | 16 | * | ||
260 | 17 | * Authors: Michael Terry <michael.terry@canonical.com> | ||
261 | 18 | */ | ||
262 | 19 | |||
263 | 20 | public class CachedImage : Gtk.Image | ||
264 | 21 | { | ||
265 | 22 | private static HashTable<Gdk.Pixbuf, Cairo.Surface> surface_table; | ||
266 | 23 | |||
267 | 24 | public static Cairo.Surface? get_cached_surface (Cairo.Context c, Gdk.Pixbuf pixbuf) | ||
268 | 25 | { | ||
269 | 26 | if (surface_table == null) | ||
270 | 27 | surface_table = new HashTable<Gdk.Pixbuf, Cairo.Surface> (direct_hash, direct_equal); | ||
271 | 28 | |||
272 | 29 | var surface = surface_table.lookup (pixbuf); | ||
273 | 30 | if (surface == null) | ||
274 | 31 | { | ||
275 | 32 | surface = new Cairo.Surface.similar (c.get_target (), Cairo.Content.COLOR_ALPHA, pixbuf.width, pixbuf.height); | ||
276 | 33 | var new_c = new Cairo.Context (surface); | ||
277 | 34 | Gdk.cairo_set_source_pixbuf (new_c, pixbuf, 0, 0); | ||
278 | 35 | new_c.paint (); | ||
279 | 36 | surface_table.insert (pixbuf, surface); | ||
280 | 37 | } | ||
281 | 38 | return surface; | ||
282 | 39 | } | ||
283 | 40 | |||
284 | 41 | public CachedImage (Gdk.Pixbuf? pixbuf) | ||
285 | 42 | { | ||
286 | 43 | Object (pixbuf: pixbuf); | ||
287 | 44 | } | ||
288 | 45 | |||
289 | 46 | public override bool draw (Cairo.Context c) | ||
290 | 47 | { | ||
291 | 48 | var cached_surface = get_cached_surface (c, pixbuf); | ||
292 | 49 | if (cached_surface != null) | ||
293 | 50 | { | ||
294 | 51 | c.set_source_surface (cached_surface, 0, 0); | ||
295 | 52 | c.paint (); | ||
296 | 53 | } | ||
297 | 54 | return false; | ||
298 | 55 | } | ||
299 | 56 | } | ||
300 | 0 | 57 | ||
301 | === modified file 'src/dash-button.vala' | |||
302 | --- src/dash-button.vala 2012-03-09 05:12:16 +0000 | |||
303 | +++ src/dash-button.vala 2012-03-13 14:51:31 +0000 | |||
304 | @@ -54,9 +54,18 @@ | |||
305 | 54 | this.text = text; | 54 | this.text = text; |
306 | 55 | 55 | ||
307 | 56 | // add chevron | 56 | // add chevron |
311 | 57 | var image = new Gtk.Image.from_file (Path.build_filename (Config.PKGDATADIR, "arrow_right.png", null)); | 57 | var path = Path.build_filename (Config.PKGDATADIR, "arrow_right.png", null); |
312 | 58 | sizes.add_widget (image); | 58 | try |
313 | 59 | hbox.add (image); | 59 | { |
314 | 60 | var pixbuf = new Gdk.Pixbuf.from_file (path); | ||
315 | 61 | var image = new CachedImage (pixbuf); | ||
316 | 62 | sizes.add_widget (image); | ||
317 | 63 | hbox.add (image); | ||
318 | 64 | } | ||
319 | 65 | catch (Error e) | ||
320 | 66 | { | ||
321 | 67 | debug ("Error loading image %s: %s", path, e.message); | ||
322 | 68 | } | ||
323 | 60 | 69 | ||
324 | 61 | hbox.show_all (); | 70 | hbox.show_all (); |
325 | 62 | add (hbox); | 71 | add (hbox); |
326 | 63 | 72 | ||
327 | === modified file 'src/fadable.vala' | |||
328 | --- src/fadable.vala 2012-02-21 17:31:59 +0000 | |||
329 | +++ src/fadable.vala 2012-03-13 14:51:31 +0000 | |||
330 | @@ -48,7 +48,7 @@ | |||
331 | 48 | 48 | ||
332 | 49 | construct | 49 | construct |
333 | 50 | { | 50 | { |
335 | 51 | timer = new AnimateTimer (AnimateTimer.INSTANT); | 51 | timer = new AnimateTimer (AnimateTimer.ease_out_quint, AnimateTimer.INSTANT); |
336 | 52 | timer.animate.connect (animate_cb); | 52 | timer.animate.connect (animate_cb); |
337 | 53 | } | 53 | } |
338 | 54 | 54 | ||
339 | 55 | 55 | ||
340 | === modified file 'src/main-window.vala' | |||
341 | --- src/main-window.vala 2012-03-09 05:00:29 +0000 | |||
342 | +++ src/main-window.vala 2012-03-13 14:51:31 +0000 | |||
343 | @@ -64,7 +64,10 @@ | |||
344 | 64 | 64 | ||
345 | 65 | user_list = new UserList (background, menubar); | 65 | user_list = new UserList (background, menubar); |
346 | 66 | user_list.expand = true; | 66 | user_list.expand = true; |
348 | 67 | user_list.user_displayed.connect (() => { | 67 | user_list.user_displayed_start.connect (() => { |
349 | 68 | change_background (); | ||
350 | 69 | }); | ||
351 | 70 | user_list.user_displayed_done.connect (() => { | ||
352 | 68 | menubar.set_layouts (user_list.selected_entry.keyboard_layouts); | 71 | menubar.set_layouts (user_list.selected_entry.keyboard_layouts); |
353 | 69 | change_background (); | 72 | change_background (); |
354 | 70 | }); | 73 | }); |
355 | @@ -145,12 +148,10 @@ | |||
356 | 145 | 148 | ||
357 | 146 | private void change_background () | 149 | private void change_background () |
358 | 147 | { | 150 | { |
359 | 148 | /* Set background after user stops scrolling */ | ||
360 | 149 | if (background.current_background != null) | 151 | if (background.current_background != null) |
361 | 150 | { | 152 | { |
365 | 151 | if (change_background_timeout != 0) | 153 | if (change_background_timeout == 0) |
366 | 152 | Source.remove (change_background_timeout); | 154 | change_background_timeout = Idle.add (change_background_timeout_cb); |
364 | 153 | change_background_timeout = Timeout.add (200, change_background_timeout_cb); | ||
367 | 154 | } | 155 | } |
368 | 155 | else | 156 | else |
369 | 156 | change_background_timeout_cb (); | 157 | change_background_timeout_cb (); |
370 | 157 | 158 | ||
371 | === modified file 'src/session-chooser.vala' | |||
372 | --- src/session-chooser.vala 2012-02-23 14:46:37 +0000 | |||
373 | +++ src/session-chooser.vala 2012-03-13 14:51:31 +0000 | |||
374 | @@ -80,7 +80,7 @@ | |||
375 | 80 | var pixbuf = get_badge (key); | 80 | var pixbuf = get_badge (key); |
376 | 81 | if (pixbuf != null) | 81 | if (pixbuf != null) |
377 | 82 | { | 82 | { |
379 | 83 | var image = new Gtk.Image.from_pixbuf (pixbuf); | 83 | var image = new CachedImage (pixbuf); |
380 | 84 | hbox.pack_start (image, false, false, 0); | 84 | hbox.pack_start (image, false, false, 0); |
381 | 85 | } | 85 | } |
382 | 86 | 86 | ||
383 | 87 | 87 | ||
384 | === modified file 'src/user-list.vala' | |||
385 | --- src/user-list.vala 2012-03-13 13:47:33 +0000 | |||
386 | +++ src/user-list.vala 2012-03-13 14:51:31 +0000 | |||
387 | @@ -45,6 +45,10 @@ | |||
388 | 45 | 45 | ||
389 | 46 | /* Default session for this user */ | 46 | /* Default session for this user */ |
390 | 47 | public string session; | 47 | public string session; |
391 | 48 | |||
392 | 49 | /* Cached cairo surfaces */ | ||
393 | 50 | public Cairo.Surface label_in_box_surface; | ||
394 | 51 | public Cairo.Surface label_out_of_box_surface; | ||
395 | 48 | } | 52 | } |
396 | 49 | 53 | ||
397 | 50 | private class AuthenticationMessage | 54 | private class AuthenticationMessage |
398 | @@ -113,7 +117,7 @@ | |||
399 | 113 | private DashButton login_button; | 117 | private DashButton login_button; |
400 | 114 | private Fadable prompt_widget_to_show; | 118 | private Fadable prompt_widget_to_show; |
401 | 115 | private Gtk.Button session_button; | 119 | private Gtk.Button session_button; |
403 | 116 | private Gtk.Image session_image; | 120 | private CachedImage session_image; |
404 | 117 | private SessionChooser session_chooser; | 121 | private SessionChooser session_chooser; |
405 | 118 | 122 | ||
406 | 119 | private enum Mode | 123 | private enum Mode |
407 | @@ -153,7 +157,8 @@ | |||
408 | 153 | } | 157 | } |
409 | 154 | 158 | ||
410 | 155 | public signal void user_selected (string? username); | 159 | public signal void user_selected (string? username); |
412 | 156 | public signal void user_displayed (); | 160 | public signal void user_displayed_start (); |
413 | 161 | public signal void user_displayed_done (); | ||
414 | 157 | public signal void respond_to_prompt (string text); | 162 | public signal void respond_to_prompt (string text); |
415 | 158 | public signal void start_session (); | 163 | public signal void start_session (); |
416 | 159 | 164 | ||
417 | @@ -235,7 +240,7 @@ | |||
418 | 235 | session_button = new Gtk.Button (); | 240 | session_button = new Gtk.Button (); |
419 | 236 | session_button.focus_on_click = false; | 241 | session_button.focus_on_click = false; |
420 | 237 | session_button.get_accessible ().set_name (_("Session Options")); | 242 | session_button.get_accessible ().set_name (_("Session Options")); |
422 | 238 | session_image = new Gtk.Image.from_pixbuf (get_badge ()); | 243 | session_image = new CachedImage (get_badge ()); |
423 | 239 | session_image.show (); | 244 | session_image.show (); |
424 | 240 | session_button.relief = Gtk.ReliefStyle.NONE; | 245 | session_button.relief = Gtk.ReliefStyle.NONE; |
425 | 241 | session_button.add (session_image); | 246 | session_button.add (session_image); |
426 | @@ -243,7 +248,7 @@ | |||
427 | 243 | session_button.show (); | 248 | session_button.show (); |
428 | 244 | add_with_class (session_button); | 249 | add_with_class (session_button); |
429 | 245 | 250 | ||
431 | 246 | scroll_timer = new AnimateTimer (AnimateTimer.FAST); | 251 | scroll_timer = new AnimateTimer (AnimateTimer.ease_out_quint, AnimateTimer.FAST); |
432 | 247 | scroll_timer.animate.connect (scroll_animate_cb); | 252 | scroll_timer.animate.connect (scroll_animate_cb); |
433 | 248 | } | 253 | } |
434 | 249 | 254 | ||
435 | @@ -659,6 +664,7 @@ | |||
436 | 659 | { | 664 | { |
437 | 660 | prompt_widget_to_show.fade_in (); | 665 | prompt_widget_to_show.fade_in (); |
438 | 661 | prompt_widget_to_show = null; | 666 | prompt_widget_to_show = null; |
439 | 667 | user_displayed_start (); | ||
440 | 662 | } | 668 | } |
441 | 663 | 669 | ||
442 | 664 | /* Stop when we get there */ | 670 | /* Stop when we get there */ |
443 | @@ -669,7 +675,7 @@ | |||
444 | 669 | private void finished_scrolling () | 675 | private void finished_scrolling () |
445 | 670 | { | 676 | { |
446 | 671 | session_button.show (); | 677 | session_button.show (); |
448 | 672 | user_displayed (); | 678 | user_displayed_done (); |
449 | 673 | mode = Mode.LOGIN; | 679 | mode = Mode.LOGIN; |
450 | 674 | } | 680 | } |
451 | 675 | 681 | ||
452 | @@ -687,27 +693,15 @@ | |||
453 | 687 | 693 | ||
454 | 688 | if (scroll_target_location != entries.index (entry)) | 694 | if (scroll_target_location != entries.index (entry)) |
455 | 689 | { | 695 | { |
456 | 690 | var old_target = scroll_target_location; | ||
457 | 691 | var new_target = entries.index (entry); | 696 | var new_target = entries.index (entry); |
458 | 692 | var new_direction = direction; | 697 | var new_direction = direction; |
463 | 693 | var new_start = (mode == Mode.SCROLLING) ? scroll_start_location : scroll_location; | 698 | var new_start = scroll_location; |
460 | 694 | |||
461 | 695 | if (mode == Mode.SCROLLING && scroll_direction != new_direction) | ||
462 | 696 | return; /* ignore requests when we're already scrolling the opposite way */ | ||
464 | 697 | 699 | ||
465 | 698 | if (scroll_location != new_target) | 700 | if (scroll_location != new_target) |
466 | 699 | { | 701 | { |
467 | 700 | var new_distance = new_direction * (new_target - new_start); | 702 | var new_distance = new_direction * (new_target - new_start); |
478 | 701 | if (mode == Mode.SCROLLING) | 703 | // Base rate is 350 (250 + 100). If we find ourselves going further, slow down animation |
479 | 702 | { | 704 | scroll_timer.reset (250 + int.min((int)(100 * (new_distance)), 500)); |
470 | 703 | var old_distance = new_direction * (old_target - new_start); | ||
471 | 704 | if (!scroll_timer.extend ((new_distance - old_distance) / old_distance)) | ||
472 | 705 | return; | ||
473 | 706 | } | ||
474 | 707 | else if (new_distance > 1) | ||
475 | 708 | scroll_timer.reset (600); | ||
476 | 709 | else | ||
477 | 710 | scroll_timer.reset (400); | ||
480 | 711 | 705 | ||
481 | 712 | if (prompt_entry.visible) | 706 | if (prompt_entry.visible) |
482 | 713 | prompt_widget_to_show = prompt_entry; | 707 | prompt_widget_to_show = prompt_entry; |
483 | @@ -733,7 +727,7 @@ | |||
484 | 733 | user_selected (selected_entry.name); | 727 | user_selected (selected_entry.name); |
485 | 734 | 728 | ||
486 | 735 | if (mode == Mode.LOGIN) | 729 | if (mode == Mode.LOGIN) |
488 | 736 | user_displayed (); /* didn't need to move, make sure we trigger side effects */ | 730 | user_displayed_done (); /* didn't need to move, make sure we trigger side effects */ |
489 | 737 | } | 731 | } |
490 | 738 | } | 732 | } |
491 | 739 | 733 | ||
492 | @@ -828,6 +822,47 @@ | |||
493 | 828 | session_button.size_allocate (child_allocation); | 822 | session_button.size_allocate (child_allocation); |
494 | 829 | } | 823 | } |
495 | 830 | 824 | ||
496 | 825 | private Cairo.Surface entry_ensure_label_surface (UserEntry entry, Cairo.Context orig_c, bool in_box) | ||
497 | 826 | { | ||
498 | 827 | if (in_box && entry.label_in_box_surface != null) | ||
499 | 828 | return entry.label_in_box_surface; | ||
500 | 829 | else if (!in_box && entry.label_out_of_box_surface != null) | ||
501 | 830 | return entry.label_out_of_box_surface; | ||
502 | 831 | |||
503 | 832 | int w, h; | ||
504 | 833 | entry.layout.get_pixel_size (out w, out h); | ||
505 | 834 | |||
506 | 835 | var bw = (box_width - (in_box ? 1.5 : 0.5)) * grid_size; | ||
507 | 836 | |||
508 | 837 | var surface = new Cairo.Surface.similar (orig_c.get_target (), Cairo.Content.COLOR_ALPHA, (int)(bw+1), h); | ||
509 | 838 | var c = new Cairo.Context (surface); | ||
510 | 839 | |||
511 | 840 | if (w > bw) | ||
512 | 841 | { | ||
513 | 842 | var mask = new Cairo.Pattern.linear (0, 0, bw, 0); | ||
514 | 843 | if (in_box) | ||
515 | 844 | { | ||
516 | 845 | mask.add_color_stop_rgba (1.0 - 27.0 / bw, 1.0, 1.0, 1.0, 1.0); | ||
517 | 846 | mask.add_color_stop_rgba (1.0 - 21.6 / bw, 1.0, 1.0, 1.0, 0.5); | ||
518 | 847 | } | ||
519 | 848 | else | ||
520 | 849 | mask.add_color_stop_rgba (1.0 - 64.0 / bw, 1.0, 1.0, 1.0, 1.0); | ||
521 | 850 | mask.add_color_stop_rgba (1.0, 1.0, 1.0, 1.0, 0.0); | ||
522 | 851 | c.set_source (mask); | ||
523 | 852 | } | ||
524 | 853 | else | ||
525 | 854 | c.set_source_rgba (1.0, 1.0, 1.0, 1.0); | ||
526 | 855 | |||
527 | 856 | Pango.cairo_show_layout (c, entry.layout); | ||
528 | 857 | |||
529 | 858 | if (in_box) | ||
530 | 859 | entry.label_in_box_surface = surface; | ||
531 | 860 | else | ||
532 | 861 | entry.label_out_of_box_surface = surface; | ||
533 | 862 | |||
534 | 863 | return surface; | ||
535 | 864 | } | ||
536 | 865 | |||
537 | 831 | private void draw_entry (Cairo.Context c, UserEntry entry, double alpha = 0.5, bool in_box = false, Gdk.Pixbuf? badge = null) | 866 | private void draw_entry (Cairo.Context c, UserEntry entry, double alpha = 0.5, bool in_box = false, Gdk.Pixbuf? badge = null) |
538 | 832 | { | 867 | { |
539 | 833 | c.save (); | 868 | c.save (); |
540 | @@ -848,32 +883,18 @@ | |||
541 | 848 | int w, h; | 883 | int w, h; |
542 | 849 | entry.layout.get_pixel_size (out w, out h); | 884 | entry.layout.get_pixel_size (out w, out h); |
543 | 850 | 885 | ||
570 | 851 | /* Trim label if too wide */ | 886 | var label_x = grid_size / 2; |
571 | 852 | var bw = (box_width - (in_box ? 1.1 : 0.5)) * grid_size; | 887 | var label_y = (grid_size - h) / 2 + border; |
572 | 853 | if (w > bw) | 888 | var label_surface = entry_ensure_label_surface (entry, c, in_box); |
573 | 854 | { | 889 | c.set_source_surface (label_surface, label_x, label_y); |
574 | 855 | var mask = new Cairo.Pattern.linear (0, 0, bw, 0); | 890 | c.paint_with_alpha (alpha); |
575 | 856 | if (in_box) | 891 | |
576 | 857 | { | 892 | var bw = (int) ((box_width - (in_box ? 1.5 : 0.5)) * grid_size); |
577 | 858 | mask.add_color_stop_rgba (1.0 - 27.0 / bw, 1.0, 1.0, 1.0, 1.0); | 893 | if (entry.has_messages && (!in_box || label_x + w + 6 + message_pixbuf.get_width () < bw)) |
578 | 859 | mask.add_color_stop_rgba (1.0 - 21.6 / bw, 1.0, 1.0, 1.0, 0.5); | 894 | { |
579 | 860 | } | 895 | c.translate (label_x + w + 6, label_y + (h - message_pixbuf.get_height ()) / 2); |
580 | 861 | else | 896 | var surface = CachedImage.get_cached_surface (c, message_pixbuf); |
581 | 862 | mask.add_color_stop_rgba (1.0 - 64.0 / bw, 1.0, 1.0, 1.0, alpha); | 897 | c.set_source_surface (surface, 0, 0); |
556 | 863 | mask.add_color_stop_rgba (1.0, 1.0, 1.0, 1.0, 0.0); | ||
557 | 864 | c.set_source (mask); | ||
558 | 865 | } | ||
559 | 866 | else | ||
560 | 867 | c.set_source_rgba (1.0, 1.0, 1.0, alpha); | ||
561 | 868 | |||
562 | 869 | c.translate (grid_size / 2, (grid_size - h) / 2 + border); | ||
563 | 870 | c.move_to (0, 0); | ||
564 | 871 | Pango.cairo_show_layout (c, entry.layout); | ||
565 | 872 | |||
566 | 873 | if (entry.has_messages && (!in_box || w + 6 + message_pixbuf.get_width () < bw)) | ||
567 | 874 | { | ||
568 | 875 | c.translate (w + 6, (h - message_pixbuf.get_height ()) / 2); | ||
569 | 876 | Gdk.cairo_set_source_pixbuf (c, message_pixbuf, 0, 0); | ||
582 | 877 | c.paint_with_alpha (alpha); | 898 | c.paint_with_alpha (alpha); |
583 | 878 | } | 899 | } |
584 | 879 | 900 | ||
585 | @@ -887,7 +908,8 @@ | |||
586 | 887 | /* FIXME: The 18px offset here is because the visual assets changed size from 40px to 22px. It should be fixed properly somewhere... */ | 908 | /* FIXME: The 18px offset here is because the visual assets changed size from 40px to 22px. It should be fixed properly somewhere... */ |
587 | 888 | var ypadding = (grid_size - badge.height) / 2 - 18; | 909 | var ypadding = (grid_size - badge.height) / 2 - 18; |
588 | 889 | c.translate (box_width * grid_size - grid_size - grid_size / 4 + xpadding, grid_size / 4 - ypadding - border); | 910 | c.translate (box_width * grid_size - grid_size - grid_size / 4 + xpadding, grid_size / 4 - ypadding - border); |
590 | 890 | Gdk.cairo_set_source_pixbuf (c, badge, 0, 0); | 911 | var surface = CachedImage.get_cached_surface (c, badge); |
591 | 912 | c.set_source_surface (surface, 0, 0); | ||
592 | 891 | c.paint (); | 913 | c.paint (); |
593 | 892 | c.restore (); | 914 | c.restore (); |
594 | 893 | } | 915 | } |
595 | @@ -954,7 +976,7 @@ | |||
596 | 954 | var position = index - scroll_location; | 976 | var position = index - scroll_location; |
597 | 955 | 977 | ||
598 | 956 | /* Draw entries above the box */ | 978 | /* Draw entries above the box */ |
600 | 957 | if (where == NameLocation.OUTSIDE_BOX && position < 0) | 979 | if (where == NameLocation.OUTSIDE_BOX && position < 0 && position > -1 * (int)(n_above + 1)) |
601 | 958 | { | 980 | { |
602 | 959 | var h_above = (double) (n_above + 1) * grid_size; | 981 | var h_above = (double) (n_above + 1) * grid_size; |
603 | 960 | c.save (); | 982 | c.save (); |
604 | @@ -987,7 +1009,7 @@ | |||
605 | 987 | } | 1009 | } |
606 | 988 | 1010 | ||
607 | 989 | /* Draw entries below the box */ | 1011 | /* Draw entries below the box */ |
609 | 990 | if (where == NameLocation.OUTSIDE_BOX && position > 0) | 1012 | if (where == NameLocation.OUTSIDE_BOX && position > 0 && position < n_below + 1) |
610 | 991 | { | 1013 | { |
611 | 992 | var h_below = (double) (n_below + 1) * grid_size; | 1014 | var h_below = (double) (n_below + 1) * grid_size; |
612 | 993 | c.save (); | 1015 | c.save (); |
Looks good to me and with the minimal testing I did, it functions well.
My only comment is the old code that was removed included some really great comments describing how the animations are calculated. It would be nice to have similar comments describing the current calculations. Certainly not a blocker, but the previous code comments seemed like excellent documentation of the logic used.