Merge lp:~mvo/software-center/purchaseviewspinner2 into lp:software-center
- purchaseviewspinner2
- Merge into trunk
Proposed by
Michael Vogt
Status: | Merged |
---|---|
Merged at revision: | 2602 |
Proposed branch: | lp:~mvo/software-center/purchaseviewspinner2 |
Merge into: | lp:software-center |
Diff against target: |
325 lines (+155/-7) (has conflicts) 6 files modified
softwarecenter/ui/gtk3/panes/availablepane.py (+5/-0) softwarecenter/ui/gtk3/panes/globalpane.py (+7/-0) softwarecenter/ui/gtk3/session/viewmanager.py (+18/-0) softwarecenter/ui/gtk3/views/purchaseview.py (+77/-5) test/gtk3/test_globalpane.py (+30/-0) test/gtk3/test_purchase.py (+18/-2) Text conflict in softwarecenter/ui/gtk3/views/purchaseview.py |
To merge this branch: | bzr merge lp:~mvo/software-center/purchaseviewspinner2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary Lasker (community) | Approve | ||
Michael Vogt | Pending | ||
Review via email: mp+85653@code.launchpad.net |
Commit message
Description of the change
This adds a spinner to the purchaseview both when new windows are opened and in the global toolbar.
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 'softwarecenter/ui/gtk3/panes/availablepane.py' | |||
2 | --- softwarecenter/ui/gtk3/panes/availablepane.py 2011-11-29 21:50:55 +0000 | |||
3 | +++ softwarecenter/ui/gtk3/panes/availablepane.py 2011-12-14 13:06:26 +0000 | |||
4 | @@ -138,6 +138,7 @@ | |||
5 | 138 | self.purchase_view.connect("purchase-succeeded", self.on_purchase_succeeded) | 138 | self.purchase_view.connect("purchase-succeeded", self.on_purchase_succeeded) |
6 | 139 | self.purchase_view.connect("purchase-failed", self.on_purchase_failed) | 139 | self.purchase_view.connect("purchase-failed", self.on_purchase_failed) |
7 | 140 | self.purchase_view.connect("purchase-cancelled-by-user", self.on_purchase_cancelled_by_user) | 140 | self.purchase_view.connect("purchase-cancelled-by-user", self.on_purchase_cancelled_by_user) |
8 | 141 | self.purchase_view.connect("purchase-needs-spinner", self.on_purchase_needs_spinner) | ||
9 | 141 | # categories, appview and details into the notebook in the bottom | 142 | # categories, appview and details into the notebook in the bottom |
10 | 142 | self.scroll_categories = Gtk.ScrolledWindow() | 143 | self.scroll_categories = Gtk.ScrolledWindow() |
11 | 143 | self.scroll_categories.set_policy(Gtk.PolicyType.AUTOMATIC, | 144 | self.scroll_categories.set_policy(Gtk.PolicyType.AUTOMATIC, |
12 | @@ -226,6 +227,10 @@ | |||
13 | 226 | self.display_purchase) | 227 | self.display_purchase) |
14 | 227 | return | 228 | return |
15 | 228 | 229 | ||
16 | 230 | def on_purchase_needs_spinner(self, appmanager, active): | ||
17 | 231 | vm = get_viewmanager() | ||
18 | 232 | vm.set_spinner_active(active) | ||
19 | 233 | |||
20 | 229 | def on_purchase_succeeded(self, widget): | 234 | def on_purchase_succeeded(self, widget): |
21 | 230 | # switch to the details page to display the transaction is in progress | 235 | # switch to the details page to display the transaction is in progress |
22 | 231 | self._return_to_appdetails_view() | 236 | self._return_to_appdetails_view() |
23 | 232 | 237 | ||
24 | === modified file 'softwarecenter/ui/gtk3/panes/globalpane.py' | |||
25 | --- softwarecenter/ui/gtk3/panes/globalpane.py 2011-09-15 09:36:39 +0000 | |||
26 | +++ softwarecenter/ui/gtk3/panes/globalpane.py 2011-12-14 13:06:26 +0000 | |||
27 | @@ -47,6 +47,12 @@ | |||
28 | 47 | #~ self.init_atk_name(self.searchentry, "searchentry") | 47 | #~ self.init_atk_name(self.searchentry, "searchentry") |
29 | 48 | self.searchentry = vm.get_global_searchentry() | 48 | self.searchentry = vm.get_global_searchentry() |
30 | 49 | self._insert_as_tool_item(self.searchentry, -1) | 49 | self._insert_as_tool_item(self.searchentry, -1) |
31 | 50 | |||
32 | 51 | # spinner | ||
33 | 52 | self.spinner = vm.get_global_spinner() | ||
34 | 53 | self.spinner.set_size_request(StockEms.XLARGE, StockEms.XLARGE) | ||
35 | 54 | self._insert_as_tool_item(self.spinner, -1) | ||
36 | 55 | |||
37 | 50 | if self.get_direction() != Gtk.TextDirection.RTL: | 56 | if self.get_direction() != Gtk.TextDirection.RTL: |
38 | 51 | _widget_set_margins(self.searchentry, right=StockEms.MEDIUM) | 57 | _widget_set_margins(self.searchentry, right=StockEms.MEDIUM) |
39 | 52 | else: | 58 | else: |
40 | @@ -77,6 +83,7 @@ | |||
41 | 77 | p = GlobalPane(vm, datadir, db, cache, icons) | 83 | p = GlobalPane(vm, datadir, db, cache, icons) |
42 | 78 | 84 | ||
43 | 79 | win = Gtk.Window() | 85 | win = Gtk.Window() |
44 | 86 | win.set_data("pane", p) | ||
45 | 80 | win.connect("destroy", Gtk.main_quit) | 87 | win.connect("destroy", Gtk.main_quit) |
46 | 81 | win.add(p) | 88 | win.add(p) |
47 | 82 | win.show_all() | 89 | win.show_all() |
48 | 83 | 90 | ||
49 | === modified file 'softwarecenter/ui/gtk3/session/viewmanager.py' | |||
50 | --- softwarecenter/ui/gtk3/session/viewmanager.py 2011-11-09 09:49:39 +0000 | |||
51 | +++ softwarecenter/ui/gtk3/session/viewmanager.py 2011-12-14 13:06:26 +0000 | |||
52 | @@ -49,6 +49,7 @@ | |||
53 | 49 | "right-clicked", self.on_nav_forward_clicked) | 49 | "right-clicked", self.on_nav_forward_clicked) |
54 | 50 | 50 | ||
55 | 51 | self.navhistory = NavigationHistory(self.back_forward, options) | 51 | self.navhistory = NavigationHistory(self.back_forward, options) |
56 | 52 | self.spinner = Gtk.Spinner() | ||
57 | 52 | 53 | ||
58 | 53 | self.all_views = {} | 54 | self.all_views = {} |
59 | 54 | self.view_to_pane = {} | 55 | self.view_to_pane = {} |
60 | @@ -95,9 +96,22 @@ | |||
61 | 95 | if page_id == v: | 96 | if page_id == v: |
62 | 96 | return k | 97 | return k |
63 | 97 | 98 | ||
64 | 99 | def set_spinner_active(self, active): | ||
65 | 100 | if active: | ||
66 | 101 | self.spinner.show() | ||
67 | 102 | self.spinner.start() | ||
68 | 103 | else: | ||
69 | 104 | self.spinner.stop() | ||
70 | 105 | self.spinner.hide() | ||
71 | 106 | |||
72 | 98 | def set_active_view(self, view_id): | 107 | def set_active_view(self, view_id): |
73 | 108 | # no views yet | ||
74 | 99 | if not self.all_views: | 109 | if not self.all_views: |
75 | 100 | return | 110 | return |
76 | 111 | # if the view switches, ensure that the global spinner is hidden | ||
77 | 112 | self.spinner.hide() | ||
78 | 113 | |||
79 | 114 | # emit signal | ||
80 | 101 | self.emit('view-changed', view_id) | 115 | self.emit('view-changed', view_id) |
81 | 102 | page_id = self.all_views[view_id] | 116 | page_id = self.all_views[view_id] |
82 | 103 | view_widget = self.get_view_widget(view_id) | 117 | view_widget = self.get_view_widget(view_id) |
83 | @@ -178,6 +192,7 @@ | |||
84 | 178 | self.search_entry.hide() | 192 | self.search_entry.hide() |
85 | 179 | else: | 193 | else: |
86 | 180 | self.search_entry.show() | 194 | self.search_entry.show() |
87 | 195 | self.spinner.hide() | ||
88 | 181 | return | 196 | return |
89 | 182 | 197 | ||
90 | 183 | def nav_back(self): | 198 | def nav_back(self): |
91 | @@ -194,3 +209,6 @@ | |||
92 | 194 | 209 | ||
93 | 195 | def get_global_backforward(self): | 210 | def get_global_backforward(self): |
94 | 196 | return self.back_forward | 211 | return self.back_forward |
95 | 212 | |||
96 | 213 | def get_global_spinner(self): | ||
97 | 214 | return self.spinner | ||
98 | 197 | 215 | ||
99 | === modified file 'softwarecenter/ui/gtk3/views/purchaseview.py' | |||
100 | --- softwarecenter/ui/gtk3/views/purchaseview.py 2011-12-12 09:15:52 +0000 | |||
101 | +++ softwarecenter/ui/gtk3/views/purchaseview.py 2011-12-14 13:06:26 +0000 | |||
102 | @@ -20,11 +20,13 @@ | |||
103 | 20 | from gi.repository import GObject | 20 | from gi.repository import GObject |
104 | 21 | from gi.repository import Gtk | 21 | from gi.repository import Gtk |
105 | 22 | from gi.repository import Gdk | 22 | from gi.repository import Gdk |
106 | 23 | from gi.repository import Pango | ||
107 | 23 | import logging | 24 | import logging |
108 | 24 | import os | 25 | import os |
109 | 25 | import json | 26 | import json |
110 | 26 | import sys | 27 | import sys |
111 | 27 | import urllib | 28 | import urllib |
112 | 29 | import urlparse | ||
113 | 28 | from gi.repository import WebKit as webkit | 30 | from gi.repository import WebKit as webkit |
114 | 29 | session = webkit.get_default_session() | 31 | session = webkit.get_default_session() |
115 | 30 | session.set_property("ssl-ca-file", "/etc/ssl/certs/ca-certificates.crt") | 32 | session.set_property("ssl-ca-file", "/etc/ssl/certs/ca-certificates.crt") |
116 | @@ -55,17 +57,70 @@ | |||
117 | 55 | #headers.foreach(_show_header, None) | 57 | #headers.foreach(_show_header, None) |
118 | 56 | 58 | ||
119 | 57 | 59 | ||
121 | 58 | class ScrolledWebkitWindow(Gtk.ScrolledWindow): | 60 | class ScrolledWebkitWindow(Gtk.VBox): |
122 | 59 | 61 | ||
124 | 60 | def __init__(self): | 62 | def __init__(self, include_progress_ui=False): |
125 | 61 | super(ScrolledWebkitWindow, self).__init__() | 63 | super(ScrolledWebkitWindow, self).__init__() |
126 | 64 | # get webkit | ||
127 | 62 | self.webkit = LocaleAwareWebView() | 65 | self.webkit = LocaleAwareWebView() |
128 | 63 | settings = self.webkit.get_settings() | 66 | settings = self.webkit.get_settings() |
129 | 64 | settings.set_property("enable-plugins", False) | 67 | settings.set_property("enable-plugins", False) |
131 | 65 | self.webkit.show() | 68 | # add progress UI if needed |
132 | 69 | if include_progress_ui: | ||
133 | 70 | self._add_progress_ui() | ||
134 | 71 | # create main webkitview | ||
135 | 72 | self.scroll = Gtk.ScrolledWindow() | ||
136 | 73 | self.scroll.set_policy(Gtk.PolicyType.AUTOMATIC, | ||
137 | 74 | Gtk.PolicyType.AUTOMATIC) | ||
138 | 75 | self.pack_start(self.scroll, True, True, 0) | ||
139 | 66 | # embed the webkit view in a scrolled window | 76 | # embed the webkit view in a scrolled window |
142 | 67 | self.add(self.webkit) | 77 | self.scroll.add(self.webkit) |
143 | 68 | self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) | 78 | self.show_all() |
144 | 79 | def _add_progress_ui(self): | ||
145 | 80 | # create toolbar box | ||
146 | 81 | self.header = Gtk.HBox() | ||
147 | 82 | # add spinner | ||
148 | 83 | self.spinner = Gtk.Spinner() | ||
149 | 84 | self.header.pack_start(self.spinner, False, False, 6) | ||
150 | 85 | # add a url to the toolbar | ||
151 | 86 | self.url = Gtk.Label() | ||
152 | 87 | self.url.set_ellipsize(Pango.EllipsizeMode.END) | ||
153 | 88 | self.url.set_alignment(0.0, 0.5) | ||
154 | 89 | self.url.set_text("") | ||
155 | 90 | self.header.pack_start(self.url, True, True, 0) | ||
156 | 91 | # frame around the box | ||
157 | 92 | self.frame = Gtk.Frame() | ||
158 | 93 | self.frame.set_border_width(3) | ||
159 | 94 | self.frame.add(self.header) | ||
160 | 95 | self.pack_start(self.frame, False, False, 6) | ||
161 | 96 | # connect the webkit stuff | ||
162 | 97 | self.webkit.connect("notify::uri", self._on_uri_changed) | ||
163 | 98 | self.webkit.connect("notify::load-status", self._on_load_status_changed) | ||
164 | 99 | def _on_uri_changed(self, view, pspec): | ||
165 | 100 | prop = pspec.name | ||
166 | 101 | uri = view.get_property(prop) | ||
167 | 102 | # the full uri is irellevant for the purchase view, but it is | ||
168 | 103 | # interessting to know what protocol/netloc is in use so that the | ||
169 | 104 | # user can verify its https on sites he is expecting | ||
170 | 105 | scheme, netloc, path, params, query, frag = urlparse.urlparse(uri) | ||
171 | 106 | if scheme == "file" and netloc == "": | ||
172 | 107 | self.url.set_text("") | ||
173 | 108 | else: | ||
174 | 109 | self.url.set_text("%s://%s" % (scheme, netloc)) | ||
175 | 110 | # start spinner when the uri changes | ||
176 | 111 | #self.spinner.start() | ||
177 | 112 | def _on_load_status_changed(self, view, pspec): | ||
178 | 113 | prop = pspec.name | ||
179 | 114 | status = view.get_property(prop) | ||
180 | 115 | #print status | ||
181 | 116 | if status == webkit.LoadStatus.PROVISIONAL: | ||
182 | 117 | self.spinner.start() | ||
183 | 118 | self.spinner.show() | ||
184 | 119 | if (status == webkit.LoadStatus.FINISHED or | ||
185 | 120 | status == webkit.LoadStatus.FAILED): | ||
186 | 121 | self.spinner.stop() | ||
187 | 122 | self.spinner.hide() | ||
188 | 123 | |||
189 | 69 | 124 | ||
190 | 70 | 125 | ||
191 | 71 | class PurchaseView(Gtk.VBox): | 126 | class PurchaseView(Gtk.VBox): |
192 | @@ -119,6 +174,10 @@ | |||
193 | 119 | 'purchase-cancelled-by-user' : (GObject.SignalFlags.RUN_LAST, | 174 | 'purchase-cancelled-by-user' : (GObject.SignalFlags.RUN_LAST, |
194 | 120 | None, | 175 | None, |
195 | 121 | ()), | 176 | ()), |
196 | 177 | 'purchase-needs-spinner' : (GObject.SignalFlags.RUN_LAST, | ||
197 | 178 | None, | ||
198 | 179 | (bool, )), | ||
199 | 180 | |||
200 | 122 | } | 181 | } |
201 | 123 | 182 | ||
202 | 124 | def __init__(self): | 183 | def __init__(self): |
203 | @@ -179,6 +238,7 @@ | |||
204 | 179 | 238 | ||
205 | 180 | def _on_create_web_view(self, view, frame): | 239 | def _on_create_web_view(self, view, frame): |
206 | 181 | win = Gtk.Window() | 240 | win = Gtk.Window() |
207 | 241 | <<<<<<< TREE | ||
208 | 182 | win.set_size_request(500, 400) | 242 | win.set_size_request(500, 400) |
209 | 183 | # set transient parent | 243 | # set transient parent |
210 | 184 | parent = self | 244 | parent = self |
211 | @@ -187,11 +247,20 @@ | |||
212 | 187 | win.set_transient_for(parent) | 247 | win.set_transient_for(parent) |
213 | 188 | # and add the webkit view | 248 | # and add the webkit view |
214 | 189 | wk = ScrolledWebkitWindow() | 249 | wk = ScrolledWebkitWindow() |
215 | 250 | ======= | ||
216 | 251 | win.set_size_request(400, 400) | ||
217 | 252 | wk = ScrolledWebkitWindow(include_progress_ui=True) | ||
218 | 253 | >>>>>>> MERGE-SOURCE | ||
219 | 190 | wk.webkit.connect("close-web-view", self._on_close_web_view) | 254 | wk.webkit.connect("close-web-view", self._on_close_web_view) |
220 | 191 | win.add(wk) | 255 | win.add(wk) |
221 | 192 | win.show_all() | 256 | win.show_all() |
222 | 193 | # make sure close will work later | 257 | # make sure close will work later |
223 | 194 | wk.webkit.set_data("win", win) | 258 | wk.webkit.set_data("win", win) |
224 | 259 | # find and set parent | ||
225 | 260 | w = self.wk.get_parent() | ||
226 | 261 | while w.get_parent(): | ||
227 | 262 | w = w.get_parent() | ||
228 | 263 | win.set_transient_for(w) | ||
229 | 195 | return wk.webkit | 264 | return wk.webkit |
230 | 196 | 265 | ||
231 | 197 | def _on_console_message(self, view, message, line, source_id): | 266 | def _on_console_message(self, view, message, line, source_id): |
232 | @@ -224,10 +293,13 @@ | |||
233 | 224 | prop = view.get_property(property_spec.name) | 293 | prop = view.get_property(property_spec.name) |
234 | 225 | window = self.get_window() | 294 | window = self.get_window() |
235 | 226 | if prop == webkit.LoadStatus.PROVISIONAL: | 295 | if prop == webkit.LoadStatus.PROVISIONAL: |
236 | 296 | self.emit("purchase-needs-spinner", True) | ||
237 | 227 | if window: | 297 | if window: |
238 | 228 | window.set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) | 298 | window.set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) |
239 | 229 | elif (prop == webkit.LoadStatus.FIRST_VISUALLY_NON_EMPTY_LAYOUT or | 299 | elif (prop == webkit.LoadStatus.FIRST_VISUALLY_NON_EMPTY_LAYOUT or |
240 | 300 | prop == webkit.LoadStatus.FAILED or | ||
241 | 230 | prop == webkit.LoadStatus.FINISHED): | 301 | prop == webkit.LoadStatus.FINISHED): |
242 | 302 | self.emit("purchase-needs-spinner", False) | ||
243 | 231 | if window: | 303 | if window: |
244 | 232 | window.set_cursor(None) | 304 | window.set_cursor(None) |
245 | 233 | 305 | ||
246 | 234 | 306 | ||
247 | === added file 'test/gtk3/test_globalpane.py' | |||
248 | --- test/gtk3/test_globalpane.py 1970-01-01 00:00:00 +0000 | |||
249 | +++ test/gtk3/test_globalpane.py 2011-12-14 13:06:26 +0000 | |||
250 | @@ -0,0 +1,30 @@ | |||
251 | 1 | #!/usr/bin/python | ||
252 | 2 | |||
253 | 3 | from gi.repository import Gtk, GObject | ||
254 | 4 | import sys | ||
255 | 5 | import unittest | ||
256 | 6 | |||
257 | 7 | sys.path.insert(0,"../..") | ||
258 | 8 | sys.path.insert(0,"..") | ||
259 | 9 | |||
260 | 10 | #from mock import Mock | ||
261 | 11 | |||
262 | 12 | TIMEOUT=300 | ||
263 | 13 | |||
264 | 14 | import softwarecenter.paths | ||
265 | 15 | softwarecenter.paths.datadir = "../data" | ||
266 | 16 | |||
267 | 17 | from softwarecenter.testutils import do_events | ||
268 | 18 | |||
269 | 19 | class TestGlobalPane(unittest.TestCase): | ||
270 | 20 | |||
271 | 21 | def test_spinner_available(self): | ||
272 | 22 | from softwarecenter.ui.gtk3.panes.globalpane import get_test_window | ||
273 | 23 | win = get_test_window() | ||
274 | 24 | pane = win.get_data("pane") | ||
275 | 25 | self.assertNotEqual(pane.spinner, None) | ||
276 | 26 | do_events() | ||
277 | 27 | |||
278 | 28 | |||
279 | 29 | if __name__ == "__main__": | ||
280 | 30 | unittest.main() | ||
281 | 0 | 31 | ||
282 | === modified file 'test/gtk3/test_purchase.py' | |||
283 | --- test/gtk3/test_purchase.py 2011-09-20 09:50:30 +0000 | |||
284 | +++ test/gtk3/test_purchase.py 2011-12-14 13:06:26 +0000 | |||
285 | @@ -20,6 +20,7 @@ | |||
286 | 20 | SoftwareCenterAppGtk3) | 20 | SoftwareCenterAppGtk3) |
287 | 21 | from softwarecenter.ui.gtk3.panes.availablepane import ( | 21 | from softwarecenter.ui.gtk3.panes.availablepane import ( |
288 | 22 | AvailablePane) | 22 | AvailablePane) |
289 | 23 | from softwarecenter.testutils import do_events | ||
290 | 23 | 24 | ||
291 | 24 | class TestPurchase(unittest.TestCase): | 25 | class TestPurchase(unittest.TestCase): |
292 | 25 | 26 | ||
293 | @@ -47,6 +48,22 @@ | |||
294 | 47 | # run another one | 48 | # run another one |
295 | 48 | win.destroy() | 49 | win.destroy() |
296 | 49 | 50 | ||
297 | 51 | def test_spinner_emits_signals(self): | ||
298 | 52 | import softwarecenter.ui.gtk3.views.purchaseview | ||
299 | 53 | from softwarecenter.ui.gtk3.views.purchaseview import get_test_window_purchaseview | ||
300 | 54 | win = get_test_window_purchaseview() | ||
301 | 55 | self._p() | ||
302 | 56 | # get the view | ||
303 | 57 | view = win.get_data("view") | ||
304 | 58 | # ensure "purchase-needs-spinner" signals are send | ||
305 | 59 | signal_mock = Mock() | ||
306 | 60 | view.connect("purchase-needs-spinner", signal_mock) | ||
307 | 61 | view.wk.webkit.load_uri("http://www.ubuntu.com/") | ||
308 | 62 | self._p() | ||
309 | 63 | self.assertTrue(signal_mock.called) | ||
310 | 64 | # run another one | ||
311 | 65 | win.destroy() | ||
312 | 66 | |||
313 | 50 | 67 | ||
314 | 51 | def test_reinstall_previous_purchase_display(self): | 68 | def test_reinstall_previous_purchase_display(self): |
315 | 52 | os.environ["PYTHONPATH"]=".." | 69 | os.environ["PYTHONPATH"]=".." |
316 | @@ -74,8 +91,7 @@ | |||
317 | 74 | context = GObject.main_context_default() | 91 | context = GObject.main_context_default() |
318 | 75 | for i in range(5): | 92 | for i in range(5): |
319 | 76 | time.sleep(0.1) | 93 | time.sleep(0.1) |
322 | 77 | while context.pending(): | 94 | do_events() |
321 | 78 | context.iteration() | ||
323 | 79 | 95 | ||
324 | 80 | if __name__ == "__main__": | 96 | if __name__ == "__main__": |
325 | 81 | import logging | 97 | import logging |
This is awesome! Thanks mvo!