| 481 | return self.bottom_w.get_cursor_coords(size) |
| 482 | |
| 483 | def render(self, size, focus=False): |
| 484 | maxcol, maxrow = size |
| 485 | bottom_c = self.bottom_w.render(size, focus) |
| 486 | cursor = bottom_c.cursor |
| 487 | if not cursor: |
| 488 | # Hide the tooltip if there is no cursor. |
| 489 | return bottom_c |
| 490 | |
| 491 | cursor_x, cursor_y = cursor |
| 492 | if cursor_y * 2 < maxrow: |
| 493 | # Cursor is in the top half. Tooltip goes below it: |
| 494 | y = cursor_y + 1 |
| 495 | rows = maxrow - y |
| 496 | else: |
| 497 | # Cursor is in the bottom half. Tooltip fills the area above: |
| 498 | y = 0 |
| 499 | rows = cursor_y |
| 500 | |
| 501 | # HACK: shrink-wrap the tooltip. This is ugly in multiple ways: |
| 502 | # - It only works on a listbox. |
| 503 | # - It assumes the wrapping LineBox eats one char on each edge. |
| 504 | # - It is a loop. |
| 505 | # (ideally it would check how much free space there is, |
| 506 | # instead of repeatedly trying smaller sizes) |
| 507 | while "bottom" in self.listbox.ends_visible((maxcol - 2, rows - 3)): |
| 508 | rows -= 1 |
| 509 | |
| 510 | # If we're displaying above the cursor move the top edge down: |
| 511 | if not y: |
| 512 | y = cursor_y - rows |
| 513 | |
| 514 | # Render *both* windows focused. This is probably not normal in urwid, |
| 515 | # but it works nicely. |
| 516 | top_c = self.top_w.render((maxcol, rows), focus and self.tooltip_focus) |
| 517 | |
| 518 | combi_c = urwid.CanvasOverlay(top_c, bottom_c, 0, y) |
| 519 | # Use the cursor coordinates from the bottom canvas. |
| 520 | canvas = urwid.CompositeCanvas(combi_c) |
| 521 | canvas.cursor = cursor |
| 522 | return canvas |
| 523 | |
| 524 | |
| 525 | class URWIDInteraction(repl.Interaction): |