| 1 | #---------------------------------------------------------------------------- |
| 2 | # Name: scrolledpanel.py |
| 3 | # Author: Will Sadkin |
| 4 | # Created: 03/21/2003 |
| 5 | # Copyright: (c) 2003 by Will Sadkin |
| 6 | # RCS-ID: $Id$ |
| 7 | # License: wxWindows license |
| 8 | #---------------------------------------------------------------------------- |
| 9 | # 12/11/2003 - Jeff Grimmett (grimmtooth@softhome.net) |
| 10 | # |
| 11 | # o 2.5 compatability update. |
| 12 | # |
| 13 | # 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net) |
| 14 | # |
| 15 | # o wxScrolledPanel -> ScrolledPanel |
| 16 | # |
| 17 | |
| 18 | import wx |
| 19 | |
| 20 | |
| 21 | class ScrolledPanel( wx.PyScrolledWindow ): |
| 22 | |
| 23 | """ ScrolledPanel fills a "hole" in the implementation of |
| 24 | wx.ScrolledWindow, providing automatic scrollbar and scrolling |
| 25 | behavior and the tab traversal management that wxScrolledWindow |
| 26 | lacks. This code was based on the original demo code showing how |
| 27 | to do this, but is now available for general use as a proper class |
| 28 | (and the demo is now converted to just use it.) |
| 29 | |
| 30 | It is assumed that the ScrolledPanel will have a sizer, as it is |
| 31 | used to calculate the minimal virtual size of the panel and etc. |
| 32 | """ |
| 33 | |
| 34 | def __init__(self, parent, id=-1, pos = wx.DefaultPosition, |
| 35 | size = wx.DefaultSize, style = wx.TAB_TRAVERSAL, |
| 36 | name = "scrolledpanel"): |
| 37 | |
| 38 | wx.PyScrolledWindow.__init__(self, parent, -1, |
| 39 | pos=pos, size=size, |
| 40 | style=style, name=name) |
| 41 | self.SetBestFittingSize(size) |
| 42 | self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus) |
| 43 | |
| 44 | |
| 45 | def SetupScrolling(self, scroll_x=True, scroll_y=True, rate_x=20, rate_y=20): |
| 46 | """ |
| 47 | This function sets up the event handling necessary to handle |
| 48 | scrolling properly. It should be called within the __init__ |
| 49 | function of any class that is derived from ScrolledPanel, |
| 50 | once the controls on the panel have been constructed and |
| 51 | thus the size of the scrolling area can be determined. |
| 52 | |
| 53 | """ |
| 54 | # The following is all that is needed to integrate the sizer and the |
| 55 | # scrolled window. |
| 56 | if not scroll_x: rate_x = 0 |
| 57 | if not scroll_y: rate_y = 0 |
| 58 | |
| 59 | # Round up the virtual size to be a multiple of the scroll rate |
| 60 | sizer = self.GetSizer() |
| 61 | if sizer: |
| 62 | w, h = sizer.GetMinSize() |
| 63 | if rate_x: |
| 64 | w += rate_x - (w % rate_x) |
| 65 | if rate_y: |
| 66 | h += rate_y - (h % rate_y) |
| 67 | self.SetVirtualSize( (w, h) ) |
| 68 | self.SetVirtualSizeHints( w, h ) |
| 69 | |
| 70 | self.SetScrollRate(rate_x, rate_y) |
| 71 | wx.CallAfter(self.Scroll, 0, 0) # scroll back to top after initial events |
| 72 | |
| 73 | |
| 74 | def OnChildFocus(self, evt): |
| 75 | # If the child window that gets the focus is not visible, |
| 76 | # this handler will try to scroll enough to see it. |
| 77 | evt.Skip() |
| 78 | child = evt.GetWindow() |
| 79 | |
| 80 | sppu_x, sppu_y = self.GetScrollPixelsPerUnit() |
| 81 | vs_x, vs_y = self.GetViewStart() |
| 82 | cr = child.GetRect() |
| 83 | clntsz = self.GetClientSize() |
| 84 | new_vs_x, new_vs_y = -1, -1 |
| 85 | |
| 86 | # is it before the left edge? |
| 87 | if cr.x < 0 and sppu_x > 0: |
| 88 | new_vs_x = vs_x + (cr.x / sppu_x) |
| 89 | |
| 90 | # is it above the top? |
| 91 | if cr.y < 0 and sppu_y > 0: |
| 92 | new_vs_y = vs_y + (cr.y / sppu_y) |
| 93 | |
| 94 | |
| 95 | # For the right and bottom edges, scroll enough to show the |
| 96 | # whole control if possible, but if not just scroll such that |
| 97 | # the top/left edges are still visible |
| 98 | |
| 99 | # is it past the right edge ? |
| 100 | if cr.right > clntsz.width and sppu_x > 0: |
| 101 | diff = (cr.right - clntsz.width) / sppu_x |
| 102 | if cr.x - diff * sppu_x > 0: |
| 103 | new_vs_x = vs_x + diff + 1 |
| 104 | else: |
| 105 | new_vs_x = vs_x + (cr.x / sppu_x) |
| 106 | |
| 107 | # is it below the bottom ? |
| 108 | if cr.bottom > clntsz.height and sppu_y > 0: |
| 109 | diff = (cr.bottom - clntsz.height) / sppu_y |
| 110 | if cr.y - diff * sppu_y > 0: |
| 111 | new_vs_y = vs_y + diff + 1 |
| 112 | else: |
| 113 | new_vs_y = vs_y + (cr.y / sppu_y) |
| 114 | |
| 115 | |
| 116 | # if we need to adjust |
| 117 | if new_vs_x != -1 or new_vs_y != -1: |
| 118 | #print "%s: (%s, %s)" % (self.GetName(), new_vs_x, new_vs_y) |
| 119 | self.Scroll(new_vs_x, new_vs_y) |