Merge lp:~nataliabidart/ubuntuone-control-panel/unify-loading-messages into lp:ubuntuone-control-panel

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 44
Merged at revision: 39
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/unify-loading-messages
Merge into: lp:ubuntuone-control-panel
Prerequisite: lp:~nataliabidart/ubuntuone-control-panel/remove-devices
Diff against target: 1267 lines (+423/-249)
10 files modified
data/account.ui (+92/-51)
data/controlpanel.ui (+0/-17)
data/folders.ui (+1/-14)
data/management.ui (+16/-1)
ubuntuone/controlpanel/backend.py (+10/-1)
ubuntuone/controlpanel/gtk/gui.py (+105/-70)
ubuntuone/controlpanel/gtk/tests/test_gui.py (+152/-72)
ubuntuone/controlpanel/gtk/tests/test_widgets.py (+6/-13)
ubuntuone/controlpanel/gtk/widgets.py (+15/-8)
ubuntuone/controlpanel/tests/test_backend.py (+26/-2)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/unify-loading-messages
Reviewer Review Type Date Requested Status
John Lenton (community) Approve
Roberto Alsina (community) natty fieldtest Approve
Review via email: mp+44147@code.launchpad.net

Commit message

* Updated list of messages to be shown on the overview panel (LP: #690379).

* Implemented callback for failure when loading devices.

* Added a spinner on every UbuntuOneBin to be shown while loading the panel content.

Description of the change

To run the tests, do:

./run-tests

To test IRL, open 2 terminals pointing to this branch, and run:

terminal 1: DEBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-backend
terminal 2: DEBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-gtk

and play with all the tabs except the 4th tab (not implemented yet).

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) wrote :

Works for me.

review: Approve (natty fieldtest)
41. By Natalia Bidart

UI fixes: progress bar is narrow now, less bold on Account tab, max size is
juts a size request.

42. By Natalia Bidart

Data within account section is now indented to the right.

43. By Natalia Bidart

12px indentation for account labels.

Revision history for this message
John Lenton (chipaca) wrote :

I like it better now. Still needs UI love, but at least this will probably let the UI reviewer survive long enough to give feedback :)

review: Approve
Revision history for this message
Martin Albisetti (beuno) wrote :

This is a lot, and almost everything is unrelated, so I'm not really voting :)

Header:
- When people have a lot of storage, and little used, the quota bar is basically "empty". I suggest setting the minimum to display to 5% or something noticeable
- All my files are up to date, and the text on the top-right seems to long and too shy at telling me. Maybe something shorter like "All files up-to-date", with a green tick?

Account tab:
- I would change the "Upgrade subscription" and "Support options" from links to something that makes it clearer that it will launch a browser. Maybe a small generic browser icon? Maybe an arrow at the end, hinting that you'll get taken outside of the app?
- The "upgrade subscription" could be placed underneath the
"Account type" block, as that's what you would be upgrading. It should probably be called something else, like "Buy more storage and new plans"
- "Account type" title seems leak a bit too much of our implementation lingo, maybe "Account information"?
- I think I would drop the "Name" section, and instead place that above where it says "Welcome to Ubuntu One!", like the dashboard: "Welcome to Ubuntu One, Martin Albisetti"
- I'd rename support options to "Get support"

Folders tab:
- I wouldn't center the contents, it makes it a bit harder to predict where content will be, as it moves around. I would instead top-left align it
- I'd rename "Subscribed" to "Is syncing", as "subscribed" is too much of an implementation detail
- The text on the title of this tab is too long and wraps. Maked it look awkward. It coulb be re-written to something like: Folders being watched by Ubuntu One (you don't need to clarify what "Subscribed" means if you address the above point
- The padding on the right is also different that on the left (ie, non-existent)

Devices tab:
- I love that you now cache the account information :)
- The spinner says "Loading", maybe it should say "Updating", as you are already presented with information?
- Again, I would top-left align the content
- I'd add some padding to the "Limit bandwidth" section, to split it out of the rest of the content
- Maybe the remove button could be a bit more informative. Not sure what text would be best, but maybe "Stop from syncing"? That's not great text, lets think about it.

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Martin, great feedback! really great. Thanks, I'll be addressing this in future branches.

44. By Natalia Bidart

Link buttons now work, though the linkbutotn on the overview panel is
triggering:

 GtkWarning: Unable to show '': Operation not supported

since the uri is set to ''.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/account.ui'
2--- data/account.ui 2010-11-30 20:13:10 +0000
3+++ data/account.ui 2010-12-20 21:58:42 +0000
4@@ -9,26 +9,107 @@
5 <child>
6 <object class="GtkHBox" id="hbox1">
7 <property name="visible">True</property>
8- <property name="spacing">15</property>
9+ <property name="spacing">10</property>
10 <child>
11- <object class="GtkVBox" id="vbox1">
12+ <object class="GtkVBox" id="account">
13 <property name="visible">True</property>
14 <property name="spacing">10</property>
15 <child>
16- <object class="GtkHBox" id="name_box">
17+ <object class="GtkVBox" id="data">
18 <property name="visible">True</property>
19- <property name="spacing">5</property>
20+ <property name="spacing">10</property>
21 <child>
22- <object class="GtkLabel" id="name_label">
23+ <object class="GtkVBox" id="vbox1">
24 <property name="visible">True</property>
25- <property name="xalign">0</property>
26- <property name="label">Name:</property>
27+ <child>
28+ <object class="GtkLabel" id="label1">
29+ <property name="visible">True</property>
30+ <property name="xalign">0</property>
31+ <property name="label" translatable="yes">&lt;b&gt;Name:&lt;/b&gt;</property>
32+ <property name="use_markup">True</property>
33+ </object>
34+ <packing>
35+ <property name="position">0</property>
36+ </packing>
37+ </child>
38+ <child>
39+ <object class="GtkLabel" id="name_label">
40+ <property name="visible">True</property>
41+ <property name="xalign">0</property>
42+ <property name="xpad">12</property>
43+ <property name="label">tester name</property>
44+ <property name="use_markup">True</property>
45+ </object>
46+ <packing>
47+ <property name="expand">False</property>
48+ <property name="position">1</property>
49+ </packing>
50+ </child>
51 </object>
52 <packing>
53- <property name="expand">False</property>
54 <property name="position">0</property>
55 </packing>
56 </child>
57+ <child>
58+ <object class="GtkVBox" id="vbox2">
59+ <property name="visible">True</property>
60+ <child>
61+ <object class="GtkLabel" id="label3">
62+ <property name="visible">True</property>
63+ <property name="xalign">0</property>
64+ <property name="label" translatable="yes">&lt;b&gt;Account type:&lt;/b&gt;</property>
65+ <property name="use_markup">True</property>
66+ </object>
67+ <packing>
68+ <property name="position">0</property>
69+ </packing>
70+ </child>
71+ <child>
72+ <object class="GtkLabel" id="type_label">
73+ <property name="visible">True</property>
74+ <property name="xalign">0</property>
75+ <property name="xpad">12</property>
76+ <property name="label">22GB awesomeness</property>
77+ </object>
78+ <packing>
79+ <property name="position">1</property>
80+ </packing>
81+ </child>
82+ </object>
83+ <packing>
84+ <property name="position">1</property>
85+ </packing>
86+ </child>
87+ <child>
88+ <object class="GtkVBox" id="vbox3">
89+ <property name="visible">True</property>
90+ <child>
91+ <object class="GtkLabel" id="label2">
92+ <property name="visible">True</property>
93+ <property name="xalign">0</property>
94+ <property name="label" translatable="yes">&lt;b&gt;Email address:&lt;/b&gt;</property>
95+ <property name="use_markup">True</property>
96+ </object>
97+ <packing>
98+ <property name="position">0</property>
99+ </packing>
100+ </child>
101+ <child>
102+ <object class="GtkLabel" id="email_label">
103+ <property name="visible">True</property>
104+ <property name="xalign">0</property>
105+ <property name="xpad">12</property>
106+ <property name="label">a@example.com</property>
107+ </object>
108+ <packing>
109+ <property name="position">1</property>
110+ </packing>
111+ </child>
112+ </object>
113+ <packing>
114+ <property name="position">2</property>
115+ </packing>
116+ </child>
117 </object>
118 <packing>
119 <property name="expand">False</property>
120@@ -36,48 +117,6 @@
121 </packing>
122 </child>
123 <child>
124- <object class="GtkHBox" id="type_box">
125- <property name="visible">True</property>
126- <property name="spacing">5</property>
127- <child>
128- <object class="GtkLabel" id="label2">
129- <property name="visible">True</property>
130- <property name="xalign">0</property>
131- <property name="label" translatable="yes">Account type:</property>
132- </object>
133- <packing>
134- <property name="expand">False</property>
135- <property name="position">0</property>
136- </packing>
137- </child>
138- </object>
139- <packing>
140- <property name="expand">False</property>
141- <property name="position">1</property>
142- </packing>
143- </child>
144- <child>
145- <object class="GtkHBox" id="email_box">
146- <property name="visible">True</property>
147- <property name="spacing">5</property>
148- <child>
149- <object class="GtkLabel" id="label3">
150- <property name="visible">True</property>
151- <property name="xalign">0</property>
152- <property name="label" translatable="yes">Email address:</property>
153- </object>
154- <packing>
155- <property name="expand">False</property>
156- <property name="position">0</property>
157- </packing>
158- </child>
159- </object>
160- <packing>
161- <property name="expand">False</property>
162- <property name="position">2</property>
163- </packing>
164- </child>
165- <child>
166 <object class="GtkHButtonBox" id="hbuttonbox1">
167 <property name="layout_style">center</property>
168 <child>
169@@ -97,7 +136,7 @@
170 <packing>
171 <property name="expand">False</property>
172 <property name="pack_type">end</property>
173- <property name="position">3</property>
174+ <property name="position">1</property>
175 </packing>
176 </child>
177 </object>
178@@ -108,6 +147,7 @@
179 <child>
180 <object class="GtkVButtonBox" id="vbuttonbox1">
181 <property name="visible">True</property>
182+ <property name="spacing">10</property>
183 <property name="layout_style">center</property>
184 <child>
185 <object class="GtkLinkButton" id="linkbutton1">
186@@ -144,6 +184,7 @@
187 </object>
188 <packing>
189 <property name="expand">False</property>
190+ <property name="pack_type">end</property>
191 <property name="position">1</property>
192 </packing>
193 </child>
194
195=== removed file 'data/controlpanel.ui'
196--- data/controlpanel.ui 2010-09-17 22:23:25 +0000
197+++ data/controlpanel.ui 1970-01-01 00:00:00 +0000
198@@ -1,17 +0,0 @@
199-<?xml version="1.0" encoding="UTF-8"?>
200-<interface>
201- <requires lib="gtk+" version="2.16"/>
202- <!-- interface-naming-policy project-wide -->
203- <object class="GtkVBox" id="itself">
204- <property name="visible">True</property>
205- <child>
206- <placeholder/>
207- </child>
208- <child>
209- <placeholder/>
210- </child>
211- <child>
212- <placeholder/>
213- </child>
214- </object>
215-</interface>
216
217=== modified file 'data/folders.ui'
218--- data/folders.ui 2010-12-16 15:14:22 +0000
219+++ data/folders.ui 2010-12-20 21:58:42 +0000
220@@ -7,19 +7,6 @@
221 <property name="border_width">10</property>
222 <property name="spacing">10</property>
223 <child>
224- <object class="GtkAlignment" id="label_alignment">
225- <property name="xscale">0</property>
226- <property name="yscale">0</property>
227- <child>
228- <placeholder/>
229- </child>
230- </object>
231- <packing>
232- <property name="expand">False</property>
233- <property name="position">0</property>
234- </packing>
235- </child>
236- <child>
237 <object class="GtkScrolledWindow" id="scrolledwindow1">
238 <property name="visible">True</property>
239 <property name="can_focus">True</property>
240@@ -44,7 +31,7 @@
241 </child>
242 </object>
243 <packing>
244- <property name="position">1</property>
245+ <property name="position">0</property>
246 </packing>
247 </child>
248 </object>
249
250=== modified file 'data/management.ui'
251--- data/management.ui 2010-11-30 17:21:15 +0000
252+++ data/management.ui 2010-12-20 21:58:42 +0000
253@@ -16,8 +16,23 @@
254 <property name="border_width">10</property>
255 <property name="spacing">10</property>
256 <child>
257- <object class="GtkProgressBar" id="quota_progressbar">
258+ <object class="GtkHBox" id="quota_box">
259 <property name="visible">True</property>
260+ <child>
261+ <object class="GtkAlignment" id="alignment1">
262+ <property name="visible">True</property>
263+ <property name="xscale">0</property>
264+ <property name="yscale">0</property>
265+ <child>
266+ <object class="GtkProgressBar" id="quota_progressbar">
267+ <property name="visible">True</property>
268+ </object>
269+ </child>
270+ </object>
271+ <packing>
272+ <property name="position">0</property>
273+ </packing>
274+ </child>
275 </object>
276 <packing>
277 <property name="expand">False</property>
278
279=== modified file 'ubuntuone/controlpanel/backend.py'
280--- ubuntuone/controlpanel/backend.py 2010-12-18 17:35:52 +0000
281+++ ubuntuone/controlpanel/backend.py 2010-12-20 21:58:42 +0000
282@@ -145,7 +145,16 @@
283 result = {}
284
285 account_info = yield self.wc.call_api(ACCOUNT_API)
286- result["type"] = account_info["subscription"]["description"]
287+
288+ if "current_plan" in account_info:
289+ desc = account_info["current_plan"]
290+ elif "subscription" in account_info \
291+ and "description" in account_info["subscription"]:
292+ desc = account_info["subscription"]["description"]
293+ else:
294+ desc = ''
295+
296+ result["type"] = desc
297 result["name"] = account_info["nickname"]
298 result["email"] = account_info["email"]
299
300
301=== modified file 'ubuntuone/controlpanel/gtk/gui.py'
302--- ubuntuone/controlpanel/gtk/gui.py 2010-12-18 17:16:35 +0000
303+++ ubuntuone/controlpanel/gtk/gui.py 2010-12-20 21:58:42 +0000
304@@ -61,15 +61,20 @@
305 ORANGE = '#c95724'
306 LOADING = _('Loading...')
307 OVERVIEW_MSGS = [
308- _('<b>Ubuntu One</b> Music Store. '
309- 'A cloud enabled mobile shopping experience.\n'
310- '<small>«this is custom text #1»</small>'),
311- _('<i>Mobile</i> contacts sync. '
312- '<span foreground="red">Your portable address book.</span>\n'
313- '<small>«this is custom text #3»</small>'),
314- _('...'),
315- _('Sync data between computers.\n'
316- '<small>«this is custom text #<i>N</i>»</small>')]
317+ _('Backup and access your files from <b>any</b> computer (Ubuntu &amp; '
318+ 'Windows), mobile device (Android) or the web.'),
319+ _('A <b>portable address book</b> that unifies your most important '
320+ 'contact sources: mobile phones, social networks, email clients and '
321+ 'services.'),
322+ _('A <b>personal music library</b> that can be streamed to Android, '
323+ 'iPhone and AirPlay-enabled devices.'),
324+ _('Add to that music library with a <b>Music Store</b> that automatically '
325+ 'delivers to your personal cloud and connected devices.'),
326+ _('Keep your <b>Firefox bookmarks</b> and <b>Tomboy notes</b> in sync '
327+ 'across computers (Ubuntu &amp; Windows).'),
328+ _('<b>2GB</b> of free storage.'),
329+]
330+VALUE_ERROR = _('Value could not be retrieved.')
331 WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE
332 KILOBYTES = 1024
333
334@@ -92,8 +97,6 @@
335 class ControlPanelMixin(object):
336 """The main interface for the Ubuntu One control panel."""
337
338- VALUE_ERROR = _('Value could not be retrieved.')
339-
340 def __init__(self, filename=None):
341 bus = dbus.SessionBus()
342 try:
343@@ -154,7 +157,7 @@
344 self.set_title(self.TITLE % {'app_name': U1_APP_NAME})
345 self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
346 self.set_icon_name('ubuntuone')
347- self.set_geometry_hints(max_width=736, max_height=525) # bug #683164
348+ self.set_size_request(-1, 525) # bug #683164
349
350 self.connect('delete-event', lambda w, e: gtk.main_quit())
351 self.show()
352@@ -171,46 +174,49 @@
353 gtk.main()
354
355
356-class ControlPanel(gtk.VBox, ControlPanelMixin):
357+class ControlPanel(gtk.Notebook):
358 """The control panel per se, can be added into any other widget."""
359
360 # should not be any larger than 736x525
361
362 def __init__(self, window_id=0):
363- gtk.VBox.__init__(self)
364- ControlPanelMixin.__init__(self, filename='controlpanel.ui')
365+ gtk.Notebook.__init__(self)
366 self._window_id = window_id
367
368- self.management = None
369- self.overview = None
370+ self.set_show_tabs(False)
371+ self.set_show_border(False)
372+
373+ self.overview = OverviewPanel(window_id=self._window_id)
374+ self.insert_page(self.overview, position=0)
375+
376+ self.management = ManagementPanel()
377+ self.insert_page(self.management, position=1)
378+
379+ self.overview.connect('credentials-found',
380+ self.on_show_management_panel)
381+ self.management.connect('local-device-removed',
382+ self.on_show_overview_panel)
383+
384+ self.show()
385 self.on_show_overview_panel()
386- self.show()
387+
388+ logger.debug('%s: started (window size %r).',
389+ self.__class__.__name__, self.get_size_request())
390
391 def on_show_overview_panel(self, widget=None):
392 """Show the overview panel."""
393- for child in self.get_children():
394- self.remove(child)
395-
396- self.overview = OverviewPanel(window_id=self._window_id)
397- self.overview.connect('credentials-found',
398- self.on_show_management_panel)
399- self.add(self.overview)
400+ self.set_current_page(0)
401
402 def on_show_management_panel(self, widget=None,
403 credentials_are_new=False, token=None):
404 """Show the notebook (main panel)."""
405- if self.overview in self.get_children():
406- self.remove(self.overview)
407-
408- self.management = ManagementPanel()
409+ if self.get_current_page() == 0:
410+ self.management.load()
411 if credentials_are_new:
412 # redirect user to Folders page to review folders subscription
413 self.management.folders_button.clicked()
414
415- self.add(self.management)
416-
417- self.management.connect('local-device-removed',
418- self.on_show_overview_panel)
419+ self.next_page()
420
421
422 class UbuntuOneBin(gtk.VBox):
423@@ -226,8 +232,26 @@
424 self.title = PanelTitle(markup='<b>' + title + '</b>')
425 self.pack_start(self.title, expand=False)
426
427+ self.message = LabelLoading(LOADING)
428+ self.pack_start(self.message, expand=False)
429+
430 self.show_all()
431
432+ @log_call(logger.debug)
433+ def on_success(self, message=''):
434+ """Use this callback to stop the Loading and show 'message'."""
435+ self.message.stop()
436+ self.message.set_markup(message)
437+
438+ @log_call(logger.error)
439+ def on_error(self, message=None):
440+ """Use this callback to stop the Loading and set a warning message."""
441+ if message == None:
442+ message = VALUE_ERROR
443+
444+ self.message.stop()
445+ self.message.set_markup(WARNING_MARKUP % message)
446+
447
448 class OverviewPanel(GreyableBin, ControlPanelMixin):
449 """The overview panel. Introduces Ubuntu One to the not logged user."""
450@@ -244,7 +268,6 @@
451 BULLET = '<span foreground="%s">✔</span>' % ORANGE
452
453 def __init__(self, messages=None, window_id=0):
454- gtk.link_button_set_uri_hook(lambda *a: None)
455 GreyableBin.__init__(self)
456 ControlPanelMixin.__init__(self, filename='overview.ui')
457 self.add(self.itself)
458@@ -252,6 +275,8 @@
459 self.warning_label.set_property('xalign', 0.5)
460 self.warning_label.connect('size-allocate', self.on_size_allocate)
461
462+ self.connect_button.set_uri('')
463+
464 self._credentials_are_new = False
465 self._messages = messages
466 self._window_id = window_id
467@@ -343,8 +368,8 @@
468 @log_call(logger.info)
469 def on_credentials_found(self, app_name, credentials):
470 """SSO backend notifies of credentials found."""
471+ self.set_property('greyed', False)
472 self.emit('credentials-found', self._credentials_are_new, credentials)
473- self.hide()
474
475 @filter_by_app_name
476 @log_call(logger.info)
477@@ -389,6 +414,9 @@
478 """The account panel. The user can manage the subscription."""
479
480 TITLE = _('Welcome to Ubuntu One!')
481+ NAME = _('Name')
482+ TYPE = _('Account type')
483+ EMAIL = _('Email address')
484
485 def __init__(self):
486 UbuntuOneBin.__init__(self)
487@@ -400,31 +428,22 @@
488 self.on_account_info_ready)
489 self.backend.connect_to_signal('AccountInfoError',
490 self.on_account_info_error)
491-
492- self.name_label = LabelLoading(LOADING)
493- self.name_box.pack_start(self.name_label)
494-
495- self.type_label = LabelLoading(LOADING)
496- self.type_box.pack_start(self.type_label)
497-
498- self.email_label = LabelLoading(LOADING)
499- self.email_box.pack_start(self.email_label)
500+ self.account.hide()
501
502 @log_call(logger.debug)
503 def on_account_info_ready(self, info):
504 """Backend notifies of account info."""
505- for i in ('name', 'type', 'email'):
506+ self.on_success()
507+
508+ for i in (u'name', u'type', u'email'):
509 label = getattr(self, '%s_label' % i)
510- label.set_markup('<b>%s</b>' % info[i])
511- label.stop()
512+ label.set_markup('%s' % (info[i]))
513+ self.account.show()
514
515 @log_call(logger.error)
516 def on_account_info_error(self, error_dict=None):
517 """Backend notifies of an error when fetching account info."""
518- for i in ('name', 'type', 'email'):
519- label = getattr(self, '%s_label' % i)
520- label.set_markup(WARNING_MARKUP % self.VALUE_ERROR)
521- label.stop()
522+ self.on_error()
523
524
525 class FoldersPanel(UbuntuOneBin, ControlPanelMixin):
526@@ -445,24 +464,20 @@
527 self.backend.connect_to_signal('VolumesInfoError',
528 self.on_volumes_info_error)
529 self.volumes = None
530- self.volumes_label = LabelLoading(LOADING)
531- self.label_alignment.add(self.volumes_label)
532 self._subscribed = []
533
534 def on_volumes_info_ready(self, info):
535 """Backend notifies of volumes info."""
536- self.volumes_label.stop()
537
538 if self.volumes is not None:
539 self.folders.remove(self.volumes)
540 self.volumes = None
541
542 if not info:
543- self.volumes_label.set_markup(self.NO_VOLUMES)
544- self.label_alignment.show()
545+ self.on_success(self.NO_VOLUMES)
546 return
547 else:
548- self.label_alignment.hide()
549+ self.on_success()
550
551 self.volumes = gtk.Table(rows=len(info) + 1, columns=2)
552
553@@ -493,9 +508,7 @@
554 @log_call(logger.error)
555 def on_volumes_info_error(self, error_dict=None):
556 """Backend notifies of an error when fetching volumes info."""
557- self.volumes_label.stop()
558- self._set_warning(self.VALUE_ERROR, self.volumes_label)
559- self.label_alignment.show()
560+ self.on_error()
561
562 def on_subscribed_clicked(self, checkbutton):
563 """The user toggled 'checkbutton'."""
564@@ -507,7 +520,7 @@
565 def load(self):
566 """Load the volume list."""
567 self.backend.volumes_info()
568- self.volumes_label.start()
569+ self.message.start()
570
571
572 class Device(gtk.VBox, ControlPanelMixin):
573@@ -680,6 +693,7 @@
574
575 TITLE = _('The devices connected with your personal cloud network are '
576 'listed below:')
577+ NO_DEVICES = _('No devices to show.')
578
579 def __init__(self):
580 UbuntuOneBin.__init__(self)
581@@ -695,10 +709,17 @@
582 self.on_devices_info_error)
583 self.backend.connect_to_signal('DeviceRemoved',
584 self.on_device_removed)
585- self.backend.devices_info()
586
587 def on_devices_info_ready(self, info):
588 """Backend notifies of devices info."""
589+ for child in self.devices.get_children():
590+ self.devices.remove(child)
591+
592+ if not info:
593+ self.on_success(self.NO_DEVICES)
594+ else:
595+ self.on_success()
596+
597 for device_info in info:
598 device = Device()
599 device_info['device_name'] = device_info.pop('name', '')
600@@ -711,6 +732,7 @@
601 @log_call(logger.error)
602 def on_devices_info_error(self, error_dict=None):
603 """Backend notifies of an error when fetching volumes info."""
604+ self.on_error()
605
606 @log_call(logger.warning)
607 def on_device_removed(self, device_id):
608@@ -722,6 +744,11 @@
609 if child.is_local:
610 self.emit('local-device-removed')
611
612+ def load(self):
613+ """Load the device list."""
614+ self.backend.devices_info()
615+ self.message.start()
616+
617
618 class ApplicationsPanel(UbuntuOneBin, ControlPanelMixin):
619 """The applications panel."""
620@@ -735,6 +762,9 @@
621 self.add(self.itself)
622 self.show()
623
624+ self.message.stop()
625+ self.message.set_text('Under construction')
626+
627
628 class ManagementPanel(gtk.VBox, ControlPanelMixin):
629 """The management panel.
630@@ -780,9 +810,11 @@
631 self.on_file_sync_status_error)
632
633 self.header.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(DEFAULT_BG))
634+
635 self.quota_label = LabelLoading(LOADING, fg_color=DEFAULT_FG)
636+ self.quota_box.pack_start(self.quota_label, expand=False)
637+
638 self.status_label = LabelLoading(LOADING, fg_color=DEFAULT_FG)
639- self.status_box.pack_start(self.quota_label, expand=False)
640 self.status_box.pack_end(self.status_label, expand=False)
641
642 self.account = AccountPanel()
643@@ -802,12 +834,9 @@
644 self.notebook.insert_page(getattr(self, tab), position=page_num)
645
646 self.folders_button.connect('clicked', lambda b: self.folders.load())
647- sig = 'local-device-removed'
648- self.devices.connect(sig, lambda widget: self.emit(sig))
649-
650- self.backend.account_info()
651- self.backend.file_sync_status()
652- self.account_button.clicked()
653+ self.devices_button.connect('clicked', lambda b: self.devices.load())
654+ self.devices.connect('local-device-removed',
655+ lambda widget: self.emit('local-device-removed'))
656
657 def _update_quota(self, msg, data=None):
658 """Update the quota info."""
659@@ -824,6 +853,12 @@
660 self.status_label.set_markup(msg)
661 self.status_label.stop()
662
663+ def load(self):
664+ """Load the account info and file sync status list."""
665+ self.backend.account_info()
666+ self.backend.file_sync_status()
667+ self.account_button.clicked()
668+
669 @log_call(logger.debug)
670 def on_account_info_ready(self, info):
671 """Backend notifies of account info."""
672@@ -836,7 +871,7 @@
673 @log_call(logger.error)
674 def on_account_info_error(self, error_dict=None):
675 """Backend notifies of an error when fetching account info."""
676- self._update_quota(WARNING_MARKUP % self.VALUE_ERROR)
677+ self._update_quota(WARNING_MARKUP % VALUE_ERROR)
678
679 @log_call(logger.info)
680 def on_file_sync_status_disabled(self, msg):
681
682=== modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py'
683--- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-18 16:21:36 +0000
684+++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-20 21:58:42 +0000
685@@ -215,7 +215,6 @@
686 def assert_warning_correct(self, warning, text):
687 """Check that 'warning' is visible, showing 'text'."""
688 self.assertTrue(warning.get_visible(), 'Must be visible.')
689- self.assertEqual(warning.get_text(), text)
690 self.assertEqual(warning.get_label(), gui.WARNING_MARKUP % text)
691
692 def assert_function_decorated(self, decorator, func):
693@@ -294,25 +293,35 @@
694 self.assertTrue(self.ui.get_size_request() <= (736, 525))
695
696
697-class ControlPanelTestCase(ControlPanelMixinTestCase):
698+class ControlPanelTestCase(BaseTestCase):
699 """The test suite for the control panel itself."""
700
701 klass = gui.ControlPanel
702 kwargs = {'window_id': 7}
703- ui_filename = 'controlpanel.ui'
704-
705- def test_is_a_vbox(self):
706+
707+ def assert_current_tab_correct(self, expected_tab):
708+ """Check that the wiget 'expected_tab' is the current page."""
709+ actual = self.ui.get_nth_page(self.ui.get_current_page())
710+ self.assertTrue(expected_tab is actual)
711+
712+ def test_is_a_notebook(self):
713 """Inherits from gtk.VBox."""
714- self.assertIsInstance(self.ui, gui.gtk.VBox)
715+ self.assertIsInstance(self.ui, gui.gtk.Notebook)
716
717 def test_startup_visibility(self):
718 """The widget is visible at startup."""
719- self.assertTrue(self.ui.itself.get_visible(),
720+ self.assertTrue(self.ui.get_visible(),
721 'must be visible at startup.')
722
723+ def test_startup_props(self):
724+ """The tabs and border are not shown."""
725+ self.assertFalse(self.ui.get_show_border(), 'must not show border.')
726+ self.assertFalse(self.ui.get_show_tabs(), 'must not show tabs.')
727+
728 def test_overview_is_shown_at_startup(self):
729 """The overview is shown at startup."""
730 self.assertIsInstance(self.ui.overview, gui.OverviewPanel)
731+ self.assert_current_tab_correct(self.ui.overview)
732
733 def test_window_id_is_passed_to_child(self):
734 """The child gets the window_id."""
735@@ -321,15 +330,14 @@
736 def test_on_show_management_panel(self):
737 """A ManagementPanel is shown when the callback is executed."""
738 self.ui.on_show_management_panel()
739- children = self.ui.get_children()
740- self.assertIsInstance(children[-1], gui.ManagementPanel)
741+ self.assert_current_tab_correct(self.ui.management)
742
743 def test_on_show_management_panel_is_idempotent(self):
744 """Only one ManagementPanel is shown."""
745 self.ui.on_show_management_panel()
746 self.ui.on_show_management_panel()
747
748- self.assertEqual(1, len(self.ui.get_children()))
749+ self.assert_current_tab_correct(self.ui.management)
750
751 def test_credentials_found_shows_account_management_panel(self):
752 """On 'credentials-found' signal, the management panel is shown.
753@@ -337,13 +345,13 @@
754 If first signal parameter is False, visible tab should be account.
755
756 """
757- a_token = object()
758- self.ui.overview.emit('credentials-found', False, a_token)
759+ self.patch(self.ui.management, 'load', self._set_called)
760+ self.ui.overview.emit('credentials-found', False, object())
761
762- children = self.ui.get_children()
763- self.assertIsInstance(children[-1], gui.ManagementPanel)
764+ self.assert_current_tab_correct(self.ui.management)
765 self.assertEqual(self.ui.management.notebook.get_current_page(),
766 self.ui.management.ACCOUNT_PAGE)
767+ self.assertEqual(self._called, ((), {}))
768
769 def test_credentials_found_shows_folders_management_panel(self):
770 """On 'credentials-found' signal, the management panel is shown.
771@@ -354,8 +362,7 @@
772 a_token = object()
773 self.ui.overview.emit('credentials-found', True, a_token)
774
775- children = self.ui.get_children()
776- self.assertIsInstance(children[-1], gui.ManagementPanel)
777+ self.assert_current_tab_correct(self.ui.management)
778 self.assertEqual(self.ui.management.notebook.get_current_page(),
779 self.ui.management.FOLDERS_PAGE)
780
781@@ -364,9 +371,7 @@
782 self.ui.overview.emit('credentials-found', True, object())
783 self.ui.management.emit('local-device-removed')
784
785- children = self.ui.get_children()
786- self.assertEqual(1, len(children))
787- self.assertTrue(children[0] is self.ui.overview)
788+ self.assert_current_tab_correct(self.ui.overview)
789
790
791 class UbuntuOneBinTestCase(BaseTestCase):
792@@ -400,6 +405,37 @@
793 ui = self.klass() # no title given
794 self.assertEqual(ui.title.label.get_text(), '')
795
796+ def test_message_is_a_label_loading(self):
797+ """Message is the correct widget."""
798+ self.assertIsInstance(self.ui.message, gui.LabelLoading)
799+ self.assertIn(self.ui.message, self.ui.get_children())
800+
801+ def test_on_success(self):
802+ """Callback to stop the Loading and clear messages."""
803+ self.ui.on_success()
804+ self.assertEqual(self.ui.message.get_label(), '')
805+ self.assertFalse(self.ui.message.active)
806+
807+ def test_on_success_with_message(self):
808+ """Callback to stop the Loading and show a info message."""
809+ msg = 'WOW! <i>this rocks</i>'
810+ self.ui.on_success(message=msg)
811+ self.assertEqual(self.ui.message.get_label(), msg)
812+ self.assertFalse(self.ui.message.active)
813+
814+ def test_on_error(self):
815+ """Callback to stop the Loading and clear messages."""
816+ self.ui.on_error()
817+ self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
818+ self.assertFalse(self.ui.message.active)
819+
820+ def test_on_error_with_message(self):
821+ """Callback to stop the Loading and show a info message."""
822+ msg = 'WOW! <i>this does not rock</i> :-/'
823+ self.ui.on_error(message=msg)
824+ self.assert_warning_correct(self.ui.message, msg)
825+ self.assertFalse(self.ui.message.active)
826+
827
828 class OverwiewPanelTestCase(ControlPanelMixinTestCase):
829 """The test suite for the overview panel."""
830@@ -494,7 +530,7 @@
831
832 self.ui.on_credentials_found(gui.U1_APP_NAME, TOKEN)
833
834- self.assertFalse(self.ui.get_visible())
835+ self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
836 # assume credentials were in local keyring
837 self.assertEqual(self._called, ((self.ui, False, TOKEN), {}))
838
839@@ -507,7 +543,7 @@
840 # now they are!
841 self.ui.on_credentials_found(gui.U1_APP_NAME, TOKEN)
842
843- self.assertFalse(self.ui.get_visible())
844+ self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
845 # assume credentials were not in local keyring
846 self.assertEqual(self._called, ((self.ui, True, TOKEN), {}))
847
848@@ -708,11 +744,11 @@
849
850 def assert_account_info_correct(self, info):
851 """Check that the displayed account info matches 'info'."""
852- self.assertEqual(self.ui.name_label.get_text(),
853+ self.assertEqual(self.ui.name_label.get_label(),
854 FAKE_ACCOUNT_INFO['name'])
855- self.assertEqual(self.ui.type_label.get_text(),
856+ self.assertEqual(self.ui.type_label.get_label(),
857 FAKE_ACCOUNT_INFO['type'])
858- self.assertEqual(self.ui.email_label.get_text(),
859+ self.assertEqual(self.ui.email_label.get_label(),
860 FAKE_ACCOUNT_INFO['email'])
861
862 def test_is_an_ubuntuone_bin(self):
863@@ -727,15 +763,9 @@
864 """Is visible."""
865 self.assertTrue(self.ui.get_visible())
866
867- def test_type_label_is_loading(self):
868- """Placeholder for type label is a Loading widget."""
869- self.assertIsInstance(self.ui.type_label, gui.LabelLoading)
870- self.assertIn(self.ui.type_label, self.ui.type_box.get_children())
871-
872- def test_email_label_is_loading(self):
873- """Placeholder for email label is a Loading widget."""
874- self.assertIsInstance(self.ui.email_label, gui.LabelLoading)
875- self.assertIn(self.ui.email_label, self.ui.email_box.get_children())
876+ def test_account_info_is_not_visible(self):
877+ """Account info is not visible."""
878+ self.assertFalse(self.ui.account.get_visible())
879
880 def test_backend_signals(self):
881 """The proper signals are connected to the backend."""
882@@ -748,18 +778,16 @@
883 """The account info is processed when ready."""
884 self.ui.on_account_info_ready(FAKE_ACCOUNT_INFO)
885 self.assert_account_info_correct(FAKE_ACCOUNT_INFO)
886-
887- for widget in (self.ui.name_label, self.ui.type_label,
888- self.ui.email_label):
889- self.assertFalse(widget.active)
890+ self.assertTrue(self.ui.account.get_visible())
891+ self.assertFalse(self.ui.message.active)
892+ self.assertEqual(self.ui.message.get_label(), '')
893
894 def test_on_account_info_error(self):
895 """The account info couldn't be retrieved."""
896 self.ui.on_account_info_error()
897- for widget in (self.ui.name_label, self.ui.type_label,
898- self.ui.email_label):
899- self.assert_warning_correct(widget, self.ui.VALUE_ERROR)
900- self.assertFalse(widget.active)
901+ self.assertFalse(self.ui.account.get_visible())
902+ self.assertFalse(self.ui.message.active)
903+ self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR)
904
905
906 class FoldersTestCase(ControlPanelMixinTestCase):
907@@ -799,39 +827,30 @@
908
909 self.assert_backend_called('volumes_info', ())
910
911- def test_volumes_label(self):
912- """The volumes label is a LabelLoading."""
913- self.assertIsInstance(self.ui.volumes_label, gui.LabelLoading)
914- self.assertTrue(self.ui.volumes_label.active)
915- self.assertIn(self.ui.volumes_label, self.ui.label_alignment)
916-
917- def test_volumes_label_after_load(self):
918+ def test_message_after_load(self):
919 """The volumes label is active when contents are load."""
920 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
921 self.ui.load()
922
923- self.assertTrue(self.ui.volumes_label.active)
924+ self.assertTrue(self.ui.message.active)
925
926- def test_volumes_label_after_non_empty_volumes_info_ready(self):
927+ def test_message_after_non_empty_volumes_info_ready(self):
928 """The volumes label is a LabelLoading."""
929 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
930
931- self.assertFalse(self.ui.volumes_label.active)
932- self.assertFalse(self.ui.label_alignment.get_visible())
933+ self.assertFalse(self.ui.message.active)
934
935- def test_volumes_label_after_empty_volumes_info_ready(self):
936+ def test_message_after_empty_volumes_info_ready(self):
937 """When there are no volumes, a notification is shown."""
938 self.ui.on_volumes_info_ready([])
939
940- self.assertTrue(self.ui.label_alignment.get_visible())
941- self.assertFalse(self.ui.volumes_label.active)
942- self.assertEqual(self.ui.volumes_label.get_text(), self.ui.NO_VOLUMES)
943+ self.assertFalse(self.ui.message.active)
944+ self.assertEqual(self.ui.message.get_text(), self.ui.NO_VOLUMES)
945
946 def test_on_volumes_info_ready(self):
947 """The volumes info is processed when ready."""
948 self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO)
949
950- self.assertFalse(self.ui.label_alignment.get_visible())
951 self.assertEqual(self.ui.folders.get_children(), [self.ui.volumes])
952
953 volumes = self.ui.volumes.get_children()
954@@ -896,10 +915,9 @@
955 def test_on_volumes_info_error(self):
956 """The volumes info couldn't be retrieved."""
957 self.ui.on_volumes_info_error()
958- self.assert_warning_correct(warning=self.ui.volumes_label,
959- text=self.ui.VALUE_ERROR)
960- self.assertFalse(self.ui.volumes_label.active)
961- self.assertTrue(self.ui.label_alignment.get_visible())
962+ self.assert_warning_correct(warning=self.ui.message,
963+ text=gui.VALUE_ERROR)
964+ self.assertFalse(self.ui.message.active)
965
966 def test_on_volumes_info_error_after_success(self):
967 """The volumes info couldn't be retrieved after a prior success."""
968@@ -1217,10 +1235,34 @@
969 self.assertEqual(self.ui.backend._signals['DeviceRemoved'],
970 [self.ui.on_device_removed])
971
972- def test_devices_info_is_requested(self):
973+ def test_devices_info_is_requested_on_load(self):
974 """The devices info is requested to the backend."""
975+ # clean backend calls
976+ self.ui.backend._called.pop('devices_info', None)
977+ self.ui.load()
978+
979 self.assert_backend_called('devices_info', ())
980
981+ def test_message_after_load(self):
982+ """The devices label is active when contents are load."""
983+ self.ui.on_devices_info_ready(FAKE_DEVICES_INFO)
984+ self.ui.load()
985+
986+ self.assertTrue(self.ui.message.active)
987+
988+ def test_message_after_non_empty_devices_info_ready(self):
989+ """The devices label is a LabelLoading."""
990+ self.ui.on_devices_info_ready(FAKE_DEVICES_INFO)
991+
992+ self.assertFalse(self.ui.message.active)
993+
994+ def test_message_after_empty_devices_info_ready(self):
995+ """When there are no devices, a notification is shown."""
996+ self.ui.on_devices_info_ready([])
997+
998+ self.assertFalse(self.ui.message.active)
999+ self.assertEqual(self.ui.message.get_text(), self.ui.NO_DEVICES)
1000+
1001 def test_on_devices_info_ready(self):
1002 """The devices info is processed when ready."""
1003 self.ui.on_devices_info_ready(FAKE_DEVICES_INFO)
1004@@ -1258,10 +1300,35 @@
1005 for child in self.ui.devices.get_children():
1006 self.assertTrue(self.ui._devices[child.id] is child)
1007
1008+ def test_on_devices_info_ready_clears_the_list(self):
1009+ """The old devices info is cleared before updated."""
1010+ self.ui.on_devices_info_ready(FAKE_DEVICES_INFO)
1011+ self.ui.on_devices_info_ready(FAKE_DEVICES_INFO)
1012+
1013+ devices = self.ui.devices.get_children()
1014+ self.assertEqual(len(devices), len(FAKE_DEVICES_INFO))
1015+
1016+ def test_on_devices_info_ready_with_no_devices(self):
1017+ """When there are no devices, a notification is shown."""
1018+ self.ui.on_devices_info_ready([])
1019+ self.assertEqual(len(self.ui.devices.get_children()), 0)
1020+
1021 def test_on_devices_info_error(self):
1022 """The devices info couldn't be retrieved."""
1023 self.ui.on_devices_info_error()
1024- # add test!
1025+
1026+ self.assert_warning_correct(warning=self.ui.message,
1027+ text=gui.VALUE_ERROR)
1028+ self.assertFalse(self.ui.message.active)
1029+
1030+ def test_on_devices_info_error_after_success(self):
1031+ """The devices info couldn't be retrieved after a prior success."""
1032+ self.ui.on_devices_info_ready(FAKE_DEVICES_INFO)
1033+
1034+ self.ui.on_devices_info_error()
1035+
1036+ self.test_on_devices_info_error()
1037+ self.test_on_devices_info_ready_with_no_devices()
1038
1039 def test_on_device_removed(self):
1040 """When a child device was removed, remove and destroy."""
1041@@ -1393,8 +1460,9 @@
1042 self.assertEqual(self.ui.backend._signals['AccountInfoError'],
1043 [self.ui.on_account_info_error])
1044
1045- def test_account_info_is_requested(self):
1046+ def test_account_info_is_requested_on_load(self):
1047 """The account info is requested to the backend."""
1048+ self.ui.load()
1049 self.assert_backend_called('account_info', ())
1050
1051 def test_account_panel_is_packed(self):
1052@@ -1429,12 +1497,23 @@
1053
1054 self.assertEqual(self._called, ((), {}))
1055
1056- def test_placeholders_are_loading(self):
1057- """Placeholders for labels are a Loading widget."""
1058- widgets = (self.ui.quota_label, self.ui.status_label)
1059- for widget in widgets:
1060- self.assertIsInstance(widget, gui.LabelLoading)
1061- self.assertIn(widget, self.ui.status_box.get_children())
1062+ def test_entering_devices_tab_loads_content(self):
1063+ """The devices info is loaded when entering the Devices tab."""
1064+ self.patch(self.ui.devices, 'load', self._set_called)
1065+ # clean backend calls
1066+ self.ui.devices_button.clicked()
1067+
1068+ self.assertEqual(self._called, ((), {}))
1069+
1070+ def test_quota_placeholder_is_loading(self):
1071+ """Placeholders for quota label is a Loading widget."""
1072+ self.assertIsInstance(self.ui.quota_label, gui.LabelLoading)
1073+ self.assertIn(self.ui.quota_label, self.ui.quota_box.get_children())
1074+
1075+ def test_file_sync_status_placeholder_is_loading(self):
1076+ """Placeholders for file sync label is a Loading widget."""
1077+ self.assertIsInstance(self.ui.status_label, gui.LabelLoading)
1078+ self.assertIn(self.ui.status_label, self.ui.status_box.get_children())
1079
1080 def test_on_account_info_ready(self):
1081 """The account info is processed when ready."""
1082@@ -1448,7 +1527,7 @@
1083 """The account info couldn't be retrieved."""
1084 self.ui.on_account_info_error()
1085 for widget in (self.ui.quota_label,):
1086- self.assert_warning_correct(widget, self.ui.VALUE_ERROR)
1087+ self.assert_warning_correct(widget, gui.VALUE_ERROR)
1088 self.assertFalse(widget.active)
1089
1090 def test_backend_file_sync_signals(self):
1091@@ -1465,8 +1544,9 @@
1092 for sig, handlers in matches:
1093 self.assertEqual(self.ui.backend._signals[sig], handlers)
1094
1095- def test_file_sync_status_is_requested(self):
1096+ def test_file_sync_status_is_requested_on_load(self):
1097 """The file sync status is requested to the backend."""
1098+ self.ui.load()
1099 self.assert_backend_called('file_sync_status', ())
1100
1101 def test_on_file_sync_status_disabled(self):
1102
1103=== modified file 'ubuntuone/controlpanel/gtk/tests/test_widgets.py'
1104--- ubuntuone/controlpanel/gtk/tests/test_widgets.py 2010-12-02 16:23:03 +0000
1105+++ ubuntuone/controlpanel/gtk/tests/test_widgets.py 2010-12-20 21:58:42 +0000
1106@@ -75,33 +75,26 @@
1107 def test_creation(self):
1108 """A LabelLoading can be created."""
1109 self.assertEqual(self.widget.label.get_text(), '')
1110- self.assertFalse(self.widget.label.get_visible())
1111+ self.assertTrue(self.widget.label.get_visible())
1112
1113 self.assertIsInstance(self.widget.loading, widgets.Loading)
1114 self.assertTrue(self.widget.loading.get_visible())
1115- self.assertTrue(self.widget.active)
1116+ self.assertTrue(self.widget.active) # loading label is packed
1117
1118 def test_stop(self):
1119 """Stop hides the Loading widget."""
1120 self.widget.stop()
1121- self.assertTrue(self.widget.label.get_visible())
1122- self.assertFalse(self.widget.loading.get_visible())
1123+
1124+ self.assertTrue(self.widget.get_child() is self.widget.label)
1125 self.assertFalse(self.widget.active)
1126
1127 def test_start(self):
1128 """Start shows the Loading widget."""
1129 self.widget.start()
1130- self.assertFalse(self.widget.label.get_visible())
1131- self.assertTrue(self.widget.loading.get_visible())
1132+
1133+ self.assertTrue(self.widget.get_child() is self.widget.loading)
1134 self.assertTrue(self.widget.active)
1135
1136- def test_children(self):
1137- """A LabelLoading have proper children."""
1138- children = self.widget.get_children()
1139- self.assertEqual(2, len(children))
1140- self.assertTrue(self.widget.label is children[0])
1141- self.assertTrue(self.widget.loading is children[-1])
1142-
1143 def test_get_text(self):
1144 """Text can be queried."""
1145 text = 'Test me.'
1146
1147=== modified file 'ubuntuone/controlpanel/gtk/widgets.py'
1148--- ubuntuone/controlpanel/gtk/widgets.py 2010-12-06 17:39:07 +0000
1149+++ ubuntuone/controlpanel/gtk/widgets.py 2010-12-20 21:58:42 +0000
1150@@ -49,7 +49,7 @@
1151 self.show_all()
1152
1153
1154-class LabelLoading(gtk.HBox):
1155+class LabelLoading(gtk.Alignment):
1156 """A spinner and a label."""
1157
1158 def __init__(self, loading_label, fg_color=None, *args, **kwargs):
1159@@ -57,29 +57,36 @@
1160 self.loading = Loading(loading_label, fg_color=fg_color)
1161
1162 self.label = gtk.Label()
1163+ self.label.show()
1164 if fg_color is not None:
1165 self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(fg_color))
1166
1167- self.pack_start(self.label, expand=False)
1168- self.pack_start(self.loading, expand=False)
1169+ self.add(self.loading)
1170
1171 self.show()
1172+ self.set(xalign=0.5, yalign=0.5, xscale=0, yscale=0)
1173+ self.set_padding(padding_top=5, padding_bottom=0,
1174+ padding_left=5, padding_right=5)
1175 self.start()
1176
1177 @property
1178 def active(self):
1179 """Whether the Loading widget is visible or not."""
1180- return not self.label.get_visible() and self.loading.get_visible()
1181+ return self.get_child() is self.loading
1182
1183 def start(self):
1184 """Show the Loading instead of the Label widget."""
1185- self.label.hide()
1186- self.loading.show()
1187+ for child in self.get_children():
1188+ self.remove(child)
1189+
1190+ self.add(self.loading)
1191
1192 def stop(self):
1193 """Show the label instead of the Loading widget."""
1194- self.label.show()
1195- self.loading.hide()
1196+ for child in self.get_children():
1197+ self.remove(child)
1198+
1199+ self.add(self.label)
1200
1201 def set_text(self, text):
1202 """Set 'text' to be the label's text."""
1203
1204=== modified file 'ubuntuone/controlpanel/tests/test_backend.py'
1205--- ubuntuone/controlpanel/tests/test_backend.py 2010-12-18 17:35:52 +0000
1206+++ ubuntuone/controlpanel/tests/test_backend.py 2010-12-20 21:58:42 +0000
1207@@ -55,7 +55,7 @@
1208 "dbpath": "u/abc/def/12345"
1209 },
1210 "couchdb_root": "https://couchdb.one.ubuntu.com/u/abc/def/12345",
1211- "email": "andrewpz@protocultura.net",
1212+ "email": "andrewpz@protocultura.net",%s
1213 "nickname": "Andrew P. Zoilo",
1214 "id": 12345,
1215 "subscription": {
1216@@ -74,6 +74,13 @@
1217 }
1218 """
1219
1220+CURRENT_PLAN = "Ubuntu One Basic (2 GB) + 1 x 20-Pack with 20 GB (monthly)"
1221+SAMPLE_CURRENT_PLAN = '\n "current_plan": "%s",' % CURRENT_PLAN
1222+
1223+SAMPLE_ACCOUNT_NO_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % ''
1224+SAMPLE_ACCOUNT_WITH_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % SAMPLE_CURRENT_PLAN
1225+
1226+
1227 SAMPLE_QUOTA_JSON = """
1228 {
1229 "total": 53687091200,
1230@@ -89,6 +96,14 @@
1231 "email": "andrewpz@protocultura.net",
1232 }
1233
1234+EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN = {
1235+ "quota_used": "2350345156",
1236+ "quota_total": "53687091200",
1237+ "type": CURRENT_PLAN,
1238+ "name": "Andrew P. Zoilo",
1239+ "email": "andrewpz@protocultura.net",
1240+}
1241+
1242 SAMPLE_DEVICES_JSON = """
1243 [
1244 {
1245@@ -322,12 +337,21 @@
1246 def test_account_info(self):
1247 """The account_info method exercises its callback."""
1248 # pylint: disable=E1101
1249- self.be.wc.results[ACCOUNT_API] = SAMPLE_ACCOUNT_JSON
1250+ self.be.wc.results[ACCOUNT_API] = SAMPLE_ACCOUNT_NO_CURRENT_PLAN
1251 self.be.wc.results[QUOTA_API] = SAMPLE_QUOTA_JSON
1252 result = yield self.be.account_info()
1253 self.assertEqual(result, EXPECTED_ACCOUNT_INFO)
1254
1255 @inlineCallbacks
1256+ def test_account_info_with_current_plan(self):
1257+ """The account_info method exercises its callback."""
1258+ # pylint: disable=E1101
1259+ self.be.wc.results[ACCOUNT_API] = SAMPLE_ACCOUNT_WITH_CURRENT_PLAN
1260+ self.be.wc.results[QUOTA_API] = SAMPLE_QUOTA_JSON
1261+ result = yield self.be.account_info()
1262+ self.assertEqual(result, EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN)
1263+
1264+ @inlineCallbacks
1265 def test_account_info_fails(self):
1266 """The account_info method exercises its errback."""
1267 # pylint: disable=E1101

Subscribers

People subscribed via source and target branches