@@ -183,33 +183,64 @@ def get_pref_col(self, size):
183183 return urwid .Edit .get_pref_col (self , size )
184184
185185
186- class Tooltip (urwid .Overlay ):
186+ class Tooltip (urwid .BoxWidget ):
187187
188- """Exactly like Overlay but passes events to the bottom window .
188+ """Container inspired by Overlay to position our tooltip .
189189
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.
192191
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.
194194 """
195195
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+
197202 def selectable (self ):
198203 return self .bottom_w .selectable ()
199204
200205 def keypress (self , size , key ):
201- # XXX is just passing size along correct?
202206 return self .bottom_w .keypress (size , key )
203207
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+
204216 def get_cursor_coords (self , size ):
205217 return self .bottom_w .get_cursor_coords (size )
206218
207219 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
210221 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
213244 return canvas
214245
215246
@@ -360,37 +391,6 @@ def on_input_change(self, edit, text):
360391 # If we call this synchronously the get_edit_text() in repl.cw
361392 # still returns the old text...
362393 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
394394
395395 def handle_input (self , event ):
396396 if event == 'enter' :
@@ -469,9 +469,7 @@ def main(args=None, locals_=None, banner=None):
469469 tooltip = urwid .ListBox (urwid .SimpleListWalker ([
470470 urwid .Text ('' ), urwid .Text ('' ), urwid .Text ('' )]))
471471 # 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 ))
475473
476474 frame = urwid .Frame (overlay , footer = statusbar .widget )
477475
0 commit comments