@@ -183,33 +183,64 @@ def get_pref_col(self, size):
183
183
return urwid .Edit .get_pref_col (self , size )
184
184
185
185
186
- class Tooltip (urwid .Overlay ):
186
+ class Tooltip (urwid .BoxWidget ):
187
187
188
- """Exactly like Overlay but passes events to the bottom window .
188
+ """Container inspired by Overlay to position our tooltip .
189
189
190
- Also uses the cursor position from the bottom window
191
- (even if this cursor ends up on top of the top window!)
190
+ This passes keyboard events to the bottom instead of the top window.
192
191
193
- This is a quick and dirty hack.
192
+ It also positions the top window relative to the cursor position
193
+ from the bottom window and hides it if there is no cursor.
194
194
"""
195
195
196
- # TODO: mouse events
196
+ def __init__ (self , bottom_w , top_w ):
197
+ self .__super .__init__ ()
198
+
199
+ self .bottom_w = bottom_w
200
+ self .top_w = top_w
201
+
197
202
def selectable (self ):
198
203
return self .bottom_w .selectable ()
199
204
200
205
def keypress (self , size , key ):
201
- # XXX is just passing size along correct?
202
206
return self .bottom_w .keypress (size , key )
203
207
208
+ def mouse_event (self , size , event , button , col , row , focus ):
209
+ # TODO: pass to top widget if visible and inside it.
210
+ if not hasattr (self .bottom_w , 'mouse_event' ):
211
+ return False
212
+
213
+ return self .bottom_w .mouse_event (
214
+ size , event , button , col , row , focus )
215
+
204
216
def get_cursor_coords (self , size ):
205
217
return self .bottom_w .get_cursor_coords (size )
206
218
207
219
def render (self , size , focus = False ):
208
- canvas = urwid .Overlay .render (self , size , focus )
209
- # XXX HACK: re-render the bottom and steal its cursor coords
220
+ maxcol , maxrow = size
210
221
bottom_c = self .bottom_w .render (size , focus )
211
- canvas = urwid .CompositeCanvas (canvas )
212
- canvas .cursor = bottom_c .cursor
222
+ cursor = bottom_c .cursor
223
+ if not cursor :
224
+ # Hide the tooltip if there is no cursor.
225
+ return bottom_c
226
+
227
+ # TODO: deal with the tooltip not needing all the space we have.
228
+ cursor_x , cursor_y = cursor
229
+ if cursor_y * 2 < maxrow :
230
+ # Cursor is in the top half. Tooltip goes below it:
231
+ y = cursor_y + 1
232
+ rows = maxrow - y
233
+ else :
234
+ # Cursor is in the bottom half. Tooltip fills the area above:
235
+ y = 0
236
+ rows = cursor_y
237
+ # The top window never gets focus.
238
+ top_c = self .top_w .render ((maxcol , rows ))
239
+
240
+ combi_c = urwid .CanvasOverlay (top_c , bottom_c , 0 , y )
241
+ # Use the cursor coordinates from the bottom canvas.
242
+ canvas = urwid .CompositeCanvas (combi_c )
243
+ canvas .cursor = cursor
213
244
return canvas
214
245
215
246
@@ -360,37 +391,6 @@ def on_input_change(self, edit, text):
360
391
# If we call this synchronously the get_edit_text() in repl.cw
361
392
# still returns the old text...
362
393
self .main_loop .set_alarm_in (0 , self ._populate_completion )
363
- self ._reposition_tooltip ()
364
-
365
- def _reposition_tooltip (self ):
366
- # Reposition the tooltip based on cursor position.
367
- screen_cols , screen_rows = self .main_loop .screen .get_cols_rows ()
368
- # XXX this should use self.listbox.get_cursor_coords
369
- # but that doesn't exist (urwid oversight)
370
- offset , inset = self .listbox .get_focus_offset_inset (
371
- (screen_cols , screen_rows ))
372
- rel_x , rel_y = self .edit .get_cursor_coords ((screen_cols ,))
373
- y = offset + rel_y
374
- if y < 0 :
375
- # Cursor off the screen (no clue if this can happen).
376
- # Just clamp to 0.
377
- y = 0
378
-
379
- # XXX the tooltip is displayed way too huge now. The easiest way
380
- # to fix that is probably to figure out how much size the
381
- # listbox actually needs here and adjust height_amount.
382
-
383
- # XXX not sure if these overlay attributes are meant to be public...
384
- if y * 2 < screen_rows :
385
- self .overlay .valign_type = 'fixed top'
386
- self .overlay .valign_amount = y + 1
387
- self .overlay .height_type = 'fixed bottom'
388
- self .overlay .height_amount = 0
389
- else :
390
- self .overlay .valign_type = 'fixed bottom'
391
- self .overlay .valign_amount = screen_rows - y - 1
392
- self .overlay .height_type = 'fixed top'
393
- self .overlay .height_amount = 0
394
394
395
395
def handle_input (self , event ):
396
396
if event == 'enter' :
@@ -469,9 +469,7 @@ def main(args=None, locals_=None, banner=None):
469
469
tooltip = urwid .ListBox (urwid .SimpleListWalker ([
470
470
urwid .Text ('' ), urwid .Text ('' ), urwid .Text ('' )]))
471
471
# TODO: this linebox should use the 'main' color.
472
- overlay = Tooltip (urwid .LineBox (tooltip ), listbox ,
473
- 'left' , ('relative' , 100 ),
474
- ('fixed top' , 0 ), ('fixed bottom' , 0 ))
472
+ overlay = Tooltip (listbox , urwid .LineBox (tooltip ))
475
473
476
474
frame = urwid .Frame (overlay , footer = statusbar .widget )
477
475
0 commit comments