Added scrollable menus to albow, and set them as a default for ChoiceButton s
The code was heavily inspired by the that of PaletteView. My defauls are not nessecarily the best.
This commit is contained in:
parent
0a6b999a03
commit
c40d250bd3
111
albow/menu.py
111
albow/menu.py
@ -8,6 +8,7 @@ import sys
|
||||
from root import get_root, get_focus
|
||||
from dialogs import Dialog
|
||||
from theme import ThemeProperty
|
||||
from pygame import Rect, draw
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
@ -53,14 +54,25 @@ class Menu(Dialog):
|
||||
|
||||
disabled_color = ThemeProperty('disabled_color')
|
||||
click_outside_response = -1
|
||||
scroll_button_size = ThemeProperty('scroll_button_size')
|
||||
scroll_button_color = ThemeProperty('scroll_button_color')
|
||||
scroll = 0
|
||||
|
||||
def __init__(self, title, items, **kwds):
|
||||
def __init__(self, title, items, scrolling=False, scroll_items=30,
|
||||
scroll_page=5, **kwds):
|
||||
self.title = title
|
||||
self.items = items
|
||||
self._items = [MenuItem(*item) for item in items]
|
||||
self.scrolling = scrolling and len(self._items) > scroll_items
|
||||
self.scroll_items = scroll_items
|
||||
self.scroll_page = scroll_page
|
||||
Dialog.__init__(self, **kwds)
|
||||
|
||||
h = self.font.get_linesize()
|
||||
self.height = h * len(self._items) + h
|
||||
if self.scrolling:
|
||||
self.height = h * self.scroll_items + h
|
||||
else:
|
||||
self.height = h * len(self._items) + h
|
||||
|
||||
def present(self, client, pos):
|
||||
client = client or get_root()
|
||||
@ -70,7 +82,10 @@ class Menu(Dialog):
|
||||
h = font.get_linesize()
|
||||
items = self._items
|
||||
margin = self.margin
|
||||
height = h * len(items) + h
|
||||
if self.scrolling:
|
||||
height = h * self.scroll_items + h
|
||||
else:
|
||||
height = h * len(items) + h
|
||||
w1 = w2 = 0
|
||||
for item in items:
|
||||
item.enabled = self.command_is_enabled(item, focus)
|
||||
@ -80,6 +95,8 @@ class Menu(Dialog):
|
||||
self._key_margin = width
|
||||
if w2 > 0:
|
||||
width += w2 + margin
|
||||
if self.scrolling:
|
||||
width += self.scroll_button_size
|
||||
self.size = (width, height)
|
||||
self._hilited = None
|
||||
|
||||
@ -100,18 +117,42 @@ class Menu(Dialog):
|
||||
handler = handler.next_handler()
|
||||
return True
|
||||
|
||||
def scroll_up_rect(self):
|
||||
d = self.scroll_button_size
|
||||
r = Rect(0, 0, d, d)
|
||||
m = self.margin
|
||||
r.top = m
|
||||
r.right = self.width - m
|
||||
r.inflate_ip(-4, -4)
|
||||
return r
|
||||
|
||||
def scroll_down_rect(self):
|
||||
d = self.scroll_button_size
|
||||
r = Rect(0, 0, d, d)
|
||||
m = self.margin
|
||||
r.bottom = self.height - m
|
||||
r.right = self.width - m
|
||||
r.inflate_ip(-4, -4)
|
||||
return r
|
||||
|
||||
def draw(self, surf):
|
||||
font = self.font
|
||||
h = font.get_linesize()
|
||||
sep = surf.get_rect()
|
||||
sep.height = 1
|
||||
if self.scrolling:
|
||||
sep.width -= self.margin + self.scroll_button_size
|
||||
colors = [self.disabled_color, self.fg_color]
|
||||
bg = self.bg_color
|
||||
xt = self.margin
|
||||
xk = self._key_margin
|
||||
y = h // 2
|
||||
hilited = self._hilited
|
||||
for item in self._items:
|
||||
if self.scrolling:
|
||||
items = self._items[self.scroll:self.scroll + self.scroll_items]
|
||||
else:
|
||||
items = self._items
|
||||
for item in items:
|
||||
text = item.text
|
||||
if not text:
|
||||
sep.top = y + h // 2
|
||||
@ -121,6 +162,8 @@ class Menu(Dialog):
|
||||
rect = surf.get_rect()
|
||||
rect.top = y
|
||||
rect.height = h
|
||||
if self.scrolling:
|
||||
rect.width -= xt + self.scroll_button_size
|
||||
surf.fill(colors[1], rect)
|
||||
color = bg
|
||||
else:
|
||||
@ -132,6 +175,21 @@ class Menu(Dialog):
|
||||
buf = font.render(keyname, True, color)
|
||||
surf.blit(buf, (xk, y))
|
||||
y += h
|
||||
if self.scrolling:
|
||||
if self.can_scroll_up():
|
||||
self.draw_scroll_up_button(surf)
|
||||
if self.can_scroll_down():
|
||||
self.draw_scroll_down_button(surf)
|
||||
|
||||
def draw_scroll_up_button(self, surface):
|
||||
r = self.scroll_up_rect()
|
||||
c = self.scroll_button_color
|
||||
draw.polygon(surface, c, [r.bottomleft, r.midtop, r.bottomright])
|
||||
|
||||
def draw_scroll_down_button(self, surface):
|
||||
r = self.scroll_down_rect()
|
||||
c = self.scroll_button_color
|
||||
draw.polygon(surface, c, [r.topleft, r.midbottom, r.topright])
|
||||
|
||||
def mouse_move(self, e):
|
||||
self.mouse_drag(e)
|
||||
@ -143,21 +201,56 @@ class Menu(Dialog):
|
||||
self.invalidate()
|
||||
|
||||
def mouse_up(self, e):
|
||||
item = self.find_enabled_item(e)
|
||||
if item:
|
||||
self.dismiss(self._items.index(item))
|
||||
if 1 <= e.button <= 3:
|
||||
item = self.find_enabled_item(e)
|
||||
if item:
|
||||
self.dismiss(self._items.index(item))
|
||||
|
||||
def find_enabled_item(self, e):
|
||||
x, y = e.local
|
||||
if 0 <= x < self.width:
|
||||
if 0 <= x < (self.width - self.margin - self.scroll_button_size
|
||||
if self.scrolling else self.width):
|
||||
h = self.font.get_linesize()
|
||||
i = (y - h // 2) // h
|
||||
i = (y - h // 2) // h + self.scroll
|
||||
items = self._items
|
||||
if 0 <= i < len(items):
|
||||
item = items[i]
|
||||
if item.enabled:
|
||||
return item
|
||||
|
||||
def mouse_down(self, event):
|
||||
if event.button == 1:
|
||||
if self.scrolling:
|
||||
p = event.local
|
||||
if self.scroll_up_rect().collidepoint(p):
|
||||
self.scroll_up()
|
||||
return
|
||||
elif self.scroll_down_rect().collidepoint(p):
|
||||
self.scroll_down()
|
||||
return
|
||||
if event.button == 4:
|
||||
self.scroll_up()
|
||||
if event.button == 5:
|
||||
self.scroll_down()
|
||||
|
||||
Dialog.mouse_down(self, event)
|
||||
|
||||
def scroll_up(self):
|
||||
if self.can_scroll_up():
|
||||
self.scroll = max(self.scroll - self.scroll_page, 0)
|
||||
|
||||
def scroll_down(self):
|
||||
if self.can_scroll_down():
|
||||
self.scroll = min(self.scroll + self.scroll_page,
|
||||
len(self._items) - self.scroll_items)
|
||||
|
||||
def can_scroll_up(self):
|
||||
return self.scrolling and self.scroll > 0
|
||||
|
||||
def can_scroll_down(self):
|
||||
return (self.scrolling and
|
||||
self.scroll + self.scroll_items < len(self._items))
|
||||
|
||||
def find_item_for_key(self, e):
|
||||
for item in self._items:
|
||||
if item.keycode == e.key \
|
||||
|
@ -206,6 +206,8 @@ menu.fg_color = (255, 255, 255)
|
||||
menu.disabled_color = (0, 0, 0)
|
||||
menu.margin = 8
|
||||
menu.border_color = (192, 192, 192)
|
||||
menu.scroll_button_size = 16
|
||||
menu.scroll_button_color = (255, 255, 0)
|
||||
|
||||
root.MenuBar = Theme('MenuBar', base=menu)
|
||||
root.MenuBar.border_width = 0
|
||||
|
@ -413,7 +413,7 @@ class ChoiceButton(ValueButton):
|
||||
align = "c"
|
||||
choose = None
|
||||
|
||||
def __init__(self, choices, **kw):
|
||||
def __init__(self, choices, scrolling=True, scroll_items=30, **kw):
|
||||
# passing an empty list of choices is ill-advised
|
||||
|
||||
if 'choose' in kw:
|
||||
@ -421,6 +421,8 @@ class ChoiceButton(ValueButton):
|
||||
|
||||
ValueButton.__init__(self, action=self.showMenu, **kw)
|
||||
|
||||
self.scrolling = scrolling
|
||||
self.scroll_items = scroll_items
|
||||
self.choices = choices or ["[UNDEFINED]"]
|
||||
|
||||
widths = [self.font.size(c)[0] for c in choices] + [self.width]
|
||||
@ -458,7 +460,8 @@ class ChoiceButton(ValueButton):
|
||||
@choices.setter
|
||||
def choices(self, ch):
|
||||
self._choices = ch
|
||||
self.menu = Menu("", ((name, "pickMenu") for name in self._choices))
|
||||
self.menu = Menu("", ((name, "pickMenu") for name in self._choices),
|
||||
self.scrolling, self.scroll_items)
|
||||
|
||||
|
||||
def CheckBoxLabel(title, *args, **kw):
|
||||
|
Reference in New Issue
Block a user