Merge lp:~bratsche/gwibber/input-focus into lp:gwibber

Proposed by Cody Russell
Status: Merged
Merged at revision: not available
Proposed branch: lp:~bratsche/gwibber/input-focus
Merge into: lp:gwibber
Diff against target: 482 lines (+61/-56)
2 files modified
gwibber/client.py (+18/-16)
gwibber/gwui.py (+43/-40)
To merge this branch: bzr merge lp:~bratsche/gwibber/input-focus
Reviewer Review Type Date Requested Status
Ken VanDine Approve
Review via email: mp+21578@code.launchpad.net

Description of the change

Sets the focus children so that the text input widget has the focus.

To post a comment you must log in.
Revision history for this message
Ken VanDine (ken-vandine) wrote :

Awesome, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'gwibber/client.py'
2--- gwibber/client.py 2010-03-17 17:17:13 +0000
3+++ gwibber/client.py 2010-03-17 17:37:30 +0000
4@@ -26,7 +26,7 @@
5 self.ui = gtk.Builder()
6
7 self.model = gwui.Model()
8-
9+
10 self.service = self.model.daemon
11 self.service.connect_to_signal("LoadingStarted", self.on_loading_started)
12 self.service.connect_to_signal("LoadingComplete", self.on_loading_complete)
13@@ -51,7 +51,7 @@
14 self.service.Refresh()
15
16 self.setup_ui()
17-
18+
19 # set state online/offline
20 if not self.connection.isConnected():
21 log.logger.info("Setting to Offline")
22@@ -74,7 +74,7 @@
23 resources.get_ui_asset("gwibber.svg"), 24, 24))
24 self.set_icon_name("gwibber")
25 self.connect("delete-event", self.on_window_close)
26-
27+
28 # Load the application menu
29 menu_ui = self.setup_menu()
30 self.add_accel_group(menu_ui.get_accel_group())
31@@ -92,7 +92,7 @@
32
33 menubar = menu_ui.get_widget("/menubar_main")
34 menubar.append(menu_spin)
35-
36+
37 view_class = getattr(gwui,
38 "MultiStreamUi" if len(self.model.settings["streams"]) > 1
39 else "SingleStreamUi")
40@@ -104,7 +104,7 @@
41 self.stream_view.connect("stream-closed", self.on_stream_closed)
42 if isinstance(self.stream_view, gwui.MultiStreamUi):
43 self.stream_view.connect("pane-closed", self.on_pane_closed)
44-
45+
46 self.input = gwui.Input()
47 self.input.connect("submit", self.on_input_activate)
48 self.input.connect("changed", self.on_input_changed)
49@@ -113,6 +113,8 @@
50 self.input_splitter.pack1(self.stream_view, resize=True)
51 self.input_splitter.pack2(self.input, resize=False)
52
53+ self.input_splitter.set_focus_child(self.input)
54+
55 self.button_send = gtk.Button(_("Send"))
56 self.button_send.connect("clicked", self.on_button_send_clicked)
57
58@@ -125,7 +127,7 @@
59 content.pack_start(self.input_splitter, True)
60 content.pack_start(self.message_target, False)
61 content.set_border_width(5)
62-
63+
64 layout = gtk.VBox()
65 layout.pack_start(menubar, False)
66 layout.pack_start(content, True)
67@@ -147,16 +149,16 @@
68 if self.stream_view:
69 state = self.stream_view.get_state()
70 self.stream_view.destroy()
71-
72+
73 self.stream_view = self.view_class(self.model)
74 self.stream_view.connect("action", self.on_action)
75 self.stream_view.connect("search", self.on_perform_search)
76 self.stream_view.connect("stream-changed", self.on_stream_changed)
77 self.stream_view.connect("stream-closed", self.on_stream_closed)
78-
79+
80 if isinstance(self.stream_view, gwui.MultiStreamUi):
81 self.stream_view.connect("pane-closed", self.on_pane_closed)
82-
83+
84 self.input_splitter.add1(self.stream_view)
85 self.stream_view.show_all()
86 if state: self.stream_view.set_state(state)
87@@ -223,7 +225,7 @@
88 ("help_report", None, _("Report A Problem..."), None, None, lambda *a: util.load_url(BUG_URL)),
89 ])
90
91-
92+
93 ui = gtk.UIManager()
94 ui.insert_action_group(self.actions, pos=0)
95 ui.add_ui_from_string(ui_string)
96@@ -243,7 +245,7 @@
97 if "reply" in features:
98 if message["sender"].get("nick", 0) and not "thread" in features:
99 self.input.set_text("@%s: " % message["sender"]["nick"])
100-
101+
102 self.message_target.set_target(message)
103 self.input.textview.grab_focus()
104 buf = self.input.textview.get_buffer()
105@@ -254,7 +256,7 @@
106
107 def get_message(self, id):
108 return dict(self.model.messages.get_record(id).items())
109-
110+
111 def on_refresh(self, *args):
112 self.service.Refresh()
113
114@@ -262,7 +264,7 @@
115 id = self.model.streams.put_record(CouchRecord(data, kind))
116 self.model.refresh()
117 self.service.PerformOp('{"id": "%s"}' % id)
118-
119+
120 if "operation" in data:
121 stream = str(self.model.features[data["operation"]]["stream"])
122 else: stream = "search"
123@@ -348,7 +350,7 @@
124 def on_message_action_menu(self, msg):
125 theme = gtk.icon_theme_get_default()
126 menu = gtk.Menu()
127-
128+
129 for a in actions.MENU_ITEMS:
130 if a.include(self, msg):
131 image = gtk.image_new_from_icon_name(a.icon, gtk.ICON_SIZE_MENU)
132@@ -399,14 +401,14 @@
133 self.service.Send(json.dumps(data))
134 self.message_target.end()
135 else: self.service.SendMessage(text)
136-
137+
138 def on_new_stream(self, *args):
139 if isinstance(self.stream_view, gwui.MultiStreamUi):
140 self.stream_view.new_stream()
141 else:
142 self.set_view("MultiStreamUi")
143 self.stream_view.new_stream()
144-
145+
146 def on_loading_started(self, *args):
147 self.loading_spinner.set_from_animation(
148 gtk.gdk.PixbufAnimation(resources.get_ui_asset("progress.gif")))
149
150=== modified file 'gwibber/gwui.py'
151--- gwibber/gwui.py 2010-03-17 17:17:13 +0000
152+++ gwibber/gwui.py 2010-03-17 17:37:30 +0000
153@@ -30,11 +30,11 @@
154 gobject.GObject.__init__(self)
155
156 self.daemon = util.getbus("Service")
157-
158+
159 self.account_monitor = util.getbus("Accounts")
160 self.account_monitor.connect_to_signal("AccountChanged", self.on_stream_changed)
161 self.account_monitor.connect_to_signal("AccountDeleted", self.on_stream_changed)
162-
163+
164 self.streams_monitor = util.getbus("Streams")
165 self.streams_monitor.connect_to_signal("StreamChanged", self.on_stream_changed)
166 self.streams_monitor.connect_to_signal("StreamClosed", self.on_stream_changed)
167@@ -69,7 +69,7 @@
168 def find_all(self, **params):
169 for stream in self.get_streams():
170 if self.match(stream, params): yield stream
171-
172+
173 if "items" in stream:
174 for stream in stream["items"]:
175 if self.match(stream, params): yield stream
176@@ -130,7 +130,7 @@
177 }
178
179 default_streams = self.services[account["protocol"]]["default_streams"]
180-
181+
182 if len(default_streams) > 1:
183 for feature in default_streams:
184 aname = self.features[feature]["stream"]
185@@ -174,7 +174,7 @@
186 "color": util.Color(account["color"]),
187 "protocol": account["protocol"],
188 })
189-
190+
191 items.append(item)
192
193 searches = {
194@@ -192,13 +192,13 @@
195 sId = search["_id"]
196 searches["items"].append({
197 "name": search["name"],
198- "account": None,
199+ "account": None,
200 "view": self.messages.execute_view("transient_time", "messages")[[sId, {}]:[sId, 0]],
201 "stream": "search",
202 "transient": sId,
203 "color": None,
204 })
205-
206+
207 items.append(searches)
208 return items
209
210@@ -222,7 +222,7 @@
211
212 def on_click_link(self, view, frame, req):
213 uri = req.get_uri()
214-
215+
216 if uri.startswith("file:///"): return False
217 elif uri.startswith("gwibber:"):
218 url = urlparse.urlparse(uri)
219@@ -230,7 +230,7 @@
220 query = urlparse.parse_qs(url.query)
221 query = dict((x,y[0]) for x,y in query.items())
222 self.emit("action", uri, cmd, query)
223- else: util.load_url(uri)
224+ else: util.load_url(uri)
225 return True
226
227 def render(self, theme, template, **kwargs):
228@@ -245,7 +245,7 @@
229 theme_path = resources.get_theme_path(theme)
230 template_path = resources.get_template_path(template, theme)
231 lookup_paths = list(resources.get_template_dirs()) + [theme_path]
232-
233+
234 template = open(template_path).read()
235 template = Template(template, lookup=TemplateLookup(directories=lookup_paths))
236 content = template.render(theme=util.get_theme_colors(), resources=resources, _=_, **kwargs)
237@@ -253,7 +253,7 @@
238 # Avoid navigation redraw crashes
239 if isinstance(self, Navigation) and not self.get_property("visible"):
240 return content
241-
242+
243 self.load_html_string(content, "file://%s/" % os.path.dirname(template_path))
244 return content
245
246@@ -273,7 +273,7 @@
247 self.selected_stream = None
248 self.tree_enabled = False
249 self.small_icons = False
250-
251+
252 def render(self):
253 return WebUi.render(self, self.model.settings["theme"], "navigation.mako",
254 streams=self.model.get_streams(),
255@@ -334,7 +334,7 @@
256 self.splitter = gtk.HPaned()
257 self.splitter.add1(self.navigation_scroll)
258 self.splitter.add2(layout)
259-
260+
261 self.splitter.set_position(self.model.settings["sidebar_splitter"])
262 self.handle_splitter_position_change(self.model.settings["sidebar_splitter"])
263 self.splitter.connect("notify", self.on_splitter_drag)
264@@ -368,8 +368,8 @@
265 def on_splitter_drag(self, pane, ev):
266 if ev.name == 'position':
267 pos = pane.get_position()
268- self.handle_splitter_position_change(pos)
269-
270+ self.handle_splitter_position_change(pos)
271+
272 def on_stream_closed(self, widget, id, kind):
273 self.emit("stream-closed", id, kind)
274
275@@ -378,7 +378,7 @@
276 self.emit("stream-changed", stream)
277 self.update_search_visibility()
278 self.update()
279-
280+
281 def update_search_visibility(self):
282 stream = self.navigation.selected_stream
283 if stream is not None:
284@@ -428,14 +428,14 @@
285 self.scroll.add_with_viewport(self.container)
286
287 self.pack_start(self.scroll, True)
288-
289+
290 def on_stream_closed(self, widget, id, kind):
291 self.emit("stream-closed", id, kind)
292
293 def on_pane_closed(self, widget):
294 widget.destroy()
295 self.emit("pane-closed", len(self.container))
296-
297+
298 def on_search(self, widget, query):
299 self.emit("search", query)
300
301@@ -483,7 +483,7 @@
302 # Build the top navigation bar
303 close_icon = gtk.image_new_from_stock("gtk-close", gtk.ICON_SIZE_MENU)
304 down_arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE)
305-
306+
307 btn_arrow = gtk.Button()
308 btn_arrow.set_relief(gtk.RELIEF_NONE)
309 btn_arrow.add(down_arrow)
310@@ -514,7 +514,7 @@
311 # Build the main message view
312 self.message_view = MessageStreamView(self.model)
313 self.message_view.connect("action", self.on_action)
314-
315+
316 self.message_scroll = gtk.ScrolledWindow()
317 self.message_scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
318 self.message_scroll.set_shadow_type(gtk.SHADOW_IN)
319@@ -530,7 +530,7 @@
320 def on_dropdown(self, button):
321 w, h = self.arrow.window.get_geometry()[2:4]
322 x, y = self.arrow.window.get_origin()
323-
324+
325 window = gtk.Window()
326 window.move(x, y + h)
327 window.set_decorated(False)
328@@ -566,7 +566,7 @@
329
330 is_search = stream["stream"] == "search" and stream["name"] == "Search"
331 self.search_box.set_visible(not is_search)
332-
333+
334 if stream["account"]:
335 fname = resources.get_ui_asset("icons/breakdance/16x16/%s.png" % stream["protocol"])
336 self.icon_protocol.set_from_file(fname)
337@@ -604,10 +604,10 @@
338 self.model = model
339 self.model.connect("changed", self.on_account_changed)
340 self.accounts = []
341-
342+
343 self.target = None
344 self.action = None
345-
346+
347 self.targetbar = WebUi()
348 self.targetbar.connect("action", self.on_action)
349 self.targetbar.set_size_request(0, 24)
350@@ -661,11 +661,11 @@
351
352 def render(self, views):
353 accounts = CouchDatabase(COUCH_DB_ACCOUNTS).get_records(COUCH_TYPE_ACCOUNT, True)
354-
355+
356 accounts = dict((a.id, a.value) for a in accounts)
357 messages = []
358 seen = {}
359-
360+
361 for view in views:
362 view.options["descending"] = True
363 view.options["limit"] = 100
364@@ -698,8 +698,8 @@
365 else:
366 if message["txtid"]:
367 seen[message["txtid"]] = n
368-
369- WebUi.render(self, self.model.settings["theme"], "template.mako",
370+
371+ WebUi.render(self, self.model.settings["theme"], "template.mako",
372 message_store=messages,
373 preferences=self.model.settings,
374 services=self.model.services,
375@@ -712,14 +712,14 @@
376
377 def __init__(self):
378 gtk.HBox.__init__(self, spacing=2)
379-
380+
381 self.entry = gtk.Entry()
382 self.entry.connect("activate", self.on_search)
383 self.entry.connect("changed", self.on_changed)
384
385 self.button = gtk.Button("Search")
386 self.button.connect("clicked", self.on_search)
387-
388+
389 self.pack_start(self.entry, True)
390 self.pack_start(self.button, False)
391
392@@ -731,7 +731,7 @@
393 def on_search(self, *args):
394 self.emit("search", self.entry.get_text())
395 self.clear()
396-
397+
398 def clear(self):
399 self.entry.set_text("")
400
401@@ -748,7 +748,7 @@
402
403 def focus(self):
404 self.entry.grab_focus()
405-
406+
407 class Input(gtk.Frame):
408 __gsignals__ = {
409 "submit": (gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_ACTION, None, (str, int)),
410@@ -767,6 +767,9 @@
411 scroll.add(self.textview)
412 self.add(scroll)
413
414+ self.set_focus_child(scroll)
415+ scroll.set_focus_child(self.textview)
416+
417 def get_text(self):
418 return self.textview.get_text()
419
420@@ -780,7 +783,7 @@
421 text = self.textview.get_text()
422 chars = self.textview.get_char_count()
423 self.emit("changed", text, chars)
424-
425+
426 def do_submit_event(self, tv):
427 text = tv.get_text()
428 chars = tv.get_char_count()
429@@ -798,13 +801,13 @@
430
431 self.overlay_color = util.get_theme_colors()["text"].darker(3).hex
432 self.overlay_text = '<span weight="bold" size="xx-large" foreground="%s">%s</span>'
433-
434+
435 self.shortener = util.getbus("URLShorten")
436
437 self.connection = util.getbus("Connection")
438 self.connection.connect_to_signal("ConnectionOnline", self.on_connection_online)
439 self.connection.connect_to_signal("ConnectionOffline", self.on_connection_offline)
440-
441+
442 self.get_buffer().connect("insert-text", self.on_add_text)
443 self.get_buffer().connect("changed", self.on_text_changed)
444 self.connect("expose-event", self.expose_view)
445@@ -820,7 +823,7 @@
446 self.set_right_margin(2)
447 self.set_pixels_above_lines(2)
448 self.set_pixels_below_lines(2)
449-
450+
451 self.base_color = util.get_style().base[gtk.STATE_NORMAL]
452 self.error_color = gtk.gdk.color_parse("indianred")
453
454@@ -849,7 +852,7 @@
455 buf = self.get_buffer()
456 buf.stop_emission("insert-text")
457 service = self.model.settings["urlshorter"] or "is.gd"
458-
459+
460 def add_shortened(shortened_url):
461 "Internal add-shortened-url-to-buffer function: a closure"
462 iter_start = buf.get_iter_at_mark(mark_start)
463@@ -860,7 +863,7 @@
464 "Internal shortening-url-died function: a closure"
465 iter = buf.get_iter_at_mark(mark)
466 buf.insert(iter, text) # shortening failed
467-
468+
469 # set a mark at iter, so that the callback knows where to insert
470 mark_start = buf.create_mark(None, iter, True)
471 # insert a placeholder character
472@@ -870,9 +873,9 @@
473 iter_end = buf.get_iter_at_mark(buf.get_insert())
474 mark_end = buf.create_mark(None, iter_end, True)
475 self.shortener.Shorten(text, service,
476- reply_handler=add_shortened,
477+ reply_handler=add_shortened,
478 error_handler=error_shortened)
479-
480+
481 def set_overlay_text(self, text):
482 self.pango_overlay.set_markup(self.overlay_text % (self.overlay_color, text))
483