# $Id: ScrolledWindow.py,v 1.38.2.3 2007/01/26 22:51:33 marcusva Exp $
#
# Copyright (c) 2004-2007, Marcus von Appen
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""A widget class, which can contain other widgets and uses optional
scrollbars."""
from pygame import K_RIGHT, K_LEFT, K_HOME, K_END, K_DOWN, K_UP, K_PAGEDOWN
from pygame import K_PAGEUP
from ScrollBar import HScrollBar, VScrollBar
from Bin import Bin
from BaseWidget import BaseWidget
from ViewPort import ViewPort
from StyleInformation import StyleInformation
from Constants import *
import base
class ScrolledWindow (Bin):
"""ScrolledWindow (width, height) -> ScrolledWindow
A widget class, which supports horizontal and vertical scrolling.
The ScrolledWindow is a viewport, which enables its child to be
scrolled horizontally and vertically. It offers various scrolling
types, which customize the bahviour of the supplied scrollbars.
The scrolling behaviour of the ScrolledWindow can be adjusted
through the 'scrolling' attribute or set_scrolling() method and can
be one of the SCROLL_TYPES constants.
Default action (invoked by activate()):
Gives the ScrolledWindow the input focus.
Mnemonic action (invoked by activate_mnemonic()):
None
Signals:
SIG_KEYDOWN - Invoked, when a key is pressed while the ScrolledWindow
has the input focus.
SIG_MOUSEDOWN - Invoked, when a mouse button is pressed over the
ScrolledWindow.
Attributes:
scrolling - The scrolling behaviour of the ScrolledWindow.
vscrollbar - The vertical scrollbar of the ScrolledWindow.
hscrollbar - The horizontal scrollbar of the ScrolledWindow.
"""
def __init__ (self, width, height):
Bin.__init__ (self)
self._scrolling = SCROLL_AUTO
# Scrollbars.
self._vscroll = VScrollBar (height, height)
self._vscroll.connect_signal (SIG_VALCHANGED, self._scroll_child)
self._vscroll.parent = self
self._hscroll = HScrollBar (width, width)
self._hscroll.connect_signal (SIG_VALCHANGED, self._scroll_child)
self._hscroll.parent = self
self._vscroll_visible = False
self._hscroll_visible = False
# Scrolling will be handled directly by the ScrolledWindow.
self._hscroll.set_focus = lambda self: False
self._vscroll.set_focus = lambda self: False
self.controls.append (self._vscroll)
self.controls.append (self._hscroll)
self._signals[SIG_KEYDOWN] = None # Dummy
self._signals[SIG_MOUSEDOWN] = []
# Respect the size.
if width < self._hscroll.minsize[0]:
width = self._hscroll.minsize[0]
if height < self._vscroll.minsize[1]:
height = self._vscroll.minsize[1]
self.minsize = width, height
def set_scrolling (self, scrolling):
"""S.set_scrolling (...) -> None
Sets the scrolling behaviour for the ScrolledWindow.
The scrolling can be a value of the SCROLL_TYPES list.
SCROLL_AUTO causes the ScrolledList to display its scrollbars on
demand only, SCROLL_ALWAYS will show the scrollbars permanently
and SCROLL_NEVER will disable the scrollbars.
Raises a ValueError, if the passed argument is not a value of
the SCROLL_TYPES tuple.
"""
if scrolling not in SCROLL_TYPES:
raise ValueError ("scrolling must be a value from SCROLL_TYPES")
if self._scrolling != scrolling:
self._scrolling = scrolling
self.dirty = True
def set_child (self, child=None):
"""B.set_child (...) -> None
Sets the child to display in the ScrolledList.
Creates a parent-child relationship from the ScrolledList to a
widget. If the widget does not support native scrolling, it will
be packed into a ViewPort.
"""
self.lock ()
if child and not isinstance (child, ViewPort):
self.vscrollbar.value = 0
self.hscrollbar.value = 0
child = ViewPort (child)
child.minsize = self.minsize
Bin.set_child (self, child)
self.unlock ()
def activate (self):
"""S.activate () -> None
Activates the ScrolledWindow default action.
Activates the ScrolledWindow default action. This usually means
giving the ScrolledWindow the input focus.
"""
if not self.sensitive:
return
self.focus = True
def notify (self, event):
"""S.notify (...) -> None
Notifies the ScrolledWindow about an event.
"""
if not self.sensitive:
return
if event.signal in SIGNALS_MOUSE:
eventarea = self.rect_to_client ()
if event.signal == SIG_MOUSEDOWN:
if eventarea.collidepoint (event.data.pos):
self.focus = True
self.run_signal_handlers (SIG_MOUSEDOWN, event.data)
# Mouse wheel.
for c in self.controls:
c.notify (event)
if not event.handled:
if self._vscroll_visible:
if event.data.button == 4:
self.vscrollbar.decrease ()
elif event.data.button == 5:
self.vscrollbar.increase ()
elif self._hscroll_visible:
if event.data.button == 4:
self.hscrollbar.decrease ()
elif event.data.button == 5:
self.hscrollbar.increase ()
event.handled = True
elif (event.signal == SIG_KEYDOWN) and self.focus:
if self._hscroll_visible:
# Horizontal scrollbar key movement
if event.data.key == K_RIGHT:
self.hscrollbar.increase ()
event.handled = True
elif event.data.key == K_LEFT:
self.hscrollbar.decrease ()
event.handled = True
elif event.data.key == K_END:
self.hscrollbar.value = self.hscrollbar.maximum
event.handled = True
elif event.data.key == K_HOME:
self.hscrollbar.value = self.hscrollbar.minimum
event.handled = True
if self._vscroll_visible:
# Vertical scrollbar key movement
if event.data.key == K_DOWN:
self.vscrollbar.increase ()
event.handled = True
elif event.data.key == K_UP:
self.vscrollbar.decrease ()
event.handled = True
elif event.data.key == K_PAGEUP:
val = self.vscrollbar.value - 10 * self.vscrollbar.step
if val > self.vscrollbar.minimum:
self.vscrollbar.value = val
else:
self.vscrollbar.value = self.vscrollbar.minimum
event.handled = True
elif event.data.key == K_PAGEDOWN:
val = self.vscrollbar.value + 10 * self.vscrollbar.step
if val < self.vscrollbar.maximum:
self.vscrollbar.value = val
else:
self.vscrollbar.value = self.vscrollbar.maximum
event.handled = True
elif event.data.key == K_END:
self.vscrollbar.value = self.vscrollbar.maximum
event.handled = True
elif event.data.key == K_HOME:
self.vscrollbar.value = self.vscrollbar.minimum
event.handled = True
Bin.notify (self, event)
def _scroll_child (self):
"""S._scroll_child () -> None
Scrolls the child of the ScrolledWindow.
"""
if self.child != None:
self.child.lock ()
self.child.hadjustment = - int (self.hscrollbar.value)
self.child.vadjustment = - int (self.vscrollbar.value)
self.child.unlock ()
def _update_scrollbars (self):
"""S._update_scrollbars (...) -> bool, bool
Updates the size and maximum values of the attached scrollbars.
Updates the size and values of the attached scrollbars and
returns boolean values about their visibility in the order
hscrollbar, vscrollbar.
"""
old_vals = self._vscroll_visible, self._hscroll_visible
if self.scrolling == SCROLL_NEVER:
if self.child:
self.child.minsize = self.minsize
self.hscrollbar.sensitive = False
self.vscrollbar.sensitive = False
self._vscroll_visible = False
self._hscroll_visible = False
return False, False
self.hscrollbar.bottom = self.bottom - self.top
self.vscrollbar.right = self.right - self.left
width = 0
height = 0
if self.child:
width = self.child.real_width + 2 * self.padding
height = self.child.real_height + 2 * self.padding
# We are using the draw_border() method for the inner surface,
# so we have to add the borders to the scrolling maximum.
if self.scrolling == SCROLL_ALWAYS:
self.vscrollbar.minsize = (self.vscrollbar.minsize[0],
self.minsize[1] - \
self.hscrollbar.height)
self.hscrollbar.minsize = (self.minsize[0] - self.vscrollbar.width,
self.hscrollbar.minsize[1])
if self.child:
self.child.maxsize = \
(self.minsize[0] - self.vscrollbar.size[0],
self.minsize[1] - self.hscrollbar.size[1])
self.child.minsize = self.child.maxsize
if width < self.hscrollbar.minsize[0]:
width = self.hscrollbar.minsize[0]
self.hscrollbar.maximum = width
if height < self.vscrollbar.minsize[1]:
height = self.vscrollbar.minsize[1]
self.vscrollbar.maximum = height
self._vscroll_visible = True
self._hscroll_visible = True
self.vscrollbar.sensitive = True
self.hscrollbar.sensitive = True
elif self.scrolling == SCROLL_AUTO:
# Check the sizes, so we can determine, how the scrollbars
# need to be adjusted.
self._hscroll_visible = self.minsize[0] < width
self._vscroll_visible = self.minsize[1] < height
if self._hscroll_visible:
self._vscroll_visible = (self.minsize[1] -
self.hscrollbar.height) < height
if self._vscroll_visible:
self._hscroll_visible = (self.minsize[0] -
self.vscrollbar.width) < width
if self._vscroll_visible and self._hscroll_visible:
# Both scrollbars need to be shown.
self.vscrollbar.minsize = (self.vscrollbar.minsize[0],
self.minsize[1] - \
self.hscrollbar.height)
self.hscrollbar.minsize = (self.minsize[0] - \
self.vscrollbar.width,
self.hscrollbar.minsize[1])
if self.child:
self.child.maxsize = \
(self.minsize[0] - self.vscrollbar.size[0],
self.minsize[1] - self.hscrollbar.size[1])
self.child.minsize = self.child.maxsize
self.vscrollbar.maximum = height
self.hscrollbar.maximum = width
self.vscrollbar.sensitive = True
self.hscrollbar.sensitive = True
elif self._vscroll_visible:
# Only the vertical.
self.vscrollbar.minsize = (self.vscrollbar.minsize[0],
self.minsize[1])
if self.child:
self.child.maxsize = \
(self.minsize[0] - self.vscrollbar.minsize[0],
self.minsize[1])
self.child.minsize = self.child.maxsize
self.vscrollbar.maximum = height
self.vscrollbar.sensitive = True
self.hscrollbar.sensitive = False
elif self._hscroll_visible:
# Only the horizontal.
self.hscrollbar.minsize = (self.minsize[0],
self.hscrollbar.minsize[1])
if self.child:
self.child.maxsize = \
(self.minsize[0],
self.minsize[1] - self.hscrollbar.size[1])
self.child.minsize = self.child.maxsize
self.hscrollbar.maximum = width
self.hscrollbar.sensitive = True
self.vscrollbar.sensitive = False
else:
if self.child:
self.child.minsize = self.minsize
# Neither vertical nor horizontal
self.hscrollbar.sensitive = False
self.vscrollbar.sensitive = False
if not self.sensitive:
self.hscrollbar.sensitive = False
self.vscrollbar.sensitive = False
return self._hscroll_visible, self._vscroll_visible
def draw_bg (self):
"""S.draw_bg () -> Surface
Draws the ScrolledWindow background surface and returns it.
Creates the visible background surface of the ScrolledWindow and
returns it to the caller.
"""
return base.GlobalStyle.engine.draw_scrolledwindow (self)
def draw (self):
"""W.draw () -> None
Draws the ScrolledWindow surface.
Creates the visible surface of the ScrolledWindow.
"""
Bin.draw (self)
blit = self.image.blit
# Update the srollbars.
self.hscrollbar.lock ()
self.vscrollbar.lock ()
hscroll, vscroll = self._update_scrollbars ()
self.hscrollbar.unlock ()
self.vscrollbar.unlock ()
if hscroll:
blit (self.hscrollbar.image, self.hscrollbar.rect)
if vscroll:
blit (self.vscrollbar.image, self.vscrollbar.rect)
if self.child:
blit (self.child.image, self.child.rect)
scrolling = property (lambda self: self._scrolling,
lambda self, var: self.set_scrolling (var),
doc = "The scrolling behaviour for the " \
"ScrolledWindow.")
vscrollbar = property (lambda self: self._vscroll,
doc = "The vertical scrollbar.")
hscrollbar = property (lambda self: self._hscroll,
doc = "The herizontal scrollbar.")
syntax highlighted by Code2HTML, v. 0.9.1