454 lines
16 KiB
Python
454 lines
16 KiB
Python
import pygame
|
|
import view
|
|
from signalslot import Signal
|
|
|
|
|
|
class GenericUI:
|
|
def __init__(self, x, y, width, height):
|
|
self.draw_update = Signal()
|
|
|
|
self.x = x
|
|
self.y = y
|
|
self.width = width
|
|
self.height = height
|
|
self.rect = pygame.rect.Rect(x, y, width, height)
|
|
self.visible = True
|
|
self.clear_colour = (0, 0, 0)
|
|
self.outline_colour = (255, 0, 0)
|
|
self.outline_thickness = 3
|
|
self.text_colour = (255, 255, 255)
|
|
|
|
self.background = pygame.Surface((self.width, self.height))
|
|
self.background.fill(self.clear_colour)
|
|
self.background.set_colorkey(self.clear_colour)
|
|
self.background = self.background.convert()
|
|
|
|
self.hold_function = None
|
|
self.release_function = None
|
|
|
|
self.parent = None
|
|
self.children = None
|
|
|
|
def process_events(self, event):
|
|
draw_update = False
|
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
|
mouse_pos = pygame.mouse.get_pos()
|
|
if self.hold_function and self.collide_at(mouse_pos):
|
|
if event.button == 1:
|
|
self.hold_function(mouse_pos)
|
|
draw_update = True
|
|
|
|
if event.type == pygame.MOUSEBUTTONUP:
|
|
mouse_pos = pygame.mouse.get_pos()
|
|
if self.release_function and self.collide_at(mouse_pos):
|
|
if event.button == 1:
|
|
#print('mouse click')
|
|
self.release_function(mouse_pos)
|
|
draw_update = True
|
|
|
|
return draw_update
|
|
|
|
def collide_at(self, pos):
|
|
x0, y0 = self.get_offset_pos()
|
|
#print(x0, y0, pos)
|
|
rect_check = pygame.rect.Rect(x0, y0, self.rect.width, self.rect.height)
|
|
return rect_check.collidepoint(pos)
|
|
|
|
def redraw(self):
|
|
self.background.fill(self.clear_colour)
|
|
|
|
def get_pos(self):
|
|
return self.x, self.y
|
|
|
|
def get_offset_pos(self):
|
|
x, y = 0, 0
|
|
if self.parent:
|
|
x, y = self.parent.get_offset_pos()
|
|
|
|
return x+self.x, y+self.y
|
|
|
|
def set_pos(self, x, y):
|
|
self.x = x
|
|
self.y = y
|
|
self.rect.x = x
|
|
self.rect.y = y
|
|
|
|
|
|
class TextBox(GenericUI):
|
|
def __init__(self, x, y, width, height, text='Button', text_size=25):
|
|
super().__init__(x, y, width, height)
|
|
self.text = text
|
|
self.font = pygame.font.SysFont("None", text_size)
|
|
self.outline_thickness = 3
|
|
|
|
self.redraw()
|
|
|
|
def redraw(self):
|
|
super().redraw()
|
|
#if self.visible:
|
|
outline = (0, 0, self.rect.w, self.rect.h)
|
|
pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness)
|
|
rendered_text = self.font.render(self.text, True, self.text_colour).convert_alpha()
|
|
rect_center = self.background.get_rect().center
|
|
text_rect = rendered_text.get_rect(center=rect_center)
|
|
self.background.blit(rendered_text, text_rect)
|
|
|
|
def set_text(self, text):
|
|
self.text = text
|
|
self.redraw()
|
|
|
|
|
|
class Button(TextBox):
|
|
|
|
def __init__(self, x, y, width, height, text='Button', text_size=25):
|
|
self.button_down = False
|
|
self.clicked = Signal()
|
|
|
|
super().__init__(x, y, width, height, text=text, text_size=text_size)
|
|
self.hold_function = self.hold
|
|
self.release_function = self.release
|
|
|
|
self.redraw()
|
|
|
|
def redraw(self):
|
|
if self.button_down:
|
|
self.background.fill((255, 255, 255))
|
|
else:
|
|
super().redraw()
|
|
#if self.visible:
|
|
outline = (0, 0, self.rect.w, self.rect.h)
|
|
pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness)
|
|
rendered_text = self.font.render(self.text, True, self.text_colour).convert_alpha()
|
|
rect_center = self.background.get_rect().center
|
|
text_rect = rendered_text.get_rect(center=rect_center)
|
|
self.background.blit(rendered_text, text_rect)
|
|
|
|
def hold(self, *args):
|
|
if not self.button_down:
|
|
self.button_down = True
|
|
self.redraw()
|
|
|
|
def release(self, *args):
|
|
if self.button_down:
|
|
self.button_down = False
|
|
self.clicked.emit()
|
|
self.redraw()
|
|
|
|
|
|
class ScrollList(GenericUI):
|
|
|
|
def __init__(self, x, y, width, height, texts, text_size=25):
|
|
super().__init__(x, y, width, height)
|
|
self.list_selected = Signal(args=['text'])
|
|
|
|
self.font = pygame.font.SysFont("None", text_size)
|
|
self.texts = texts
|
|
self.text_rects = []
|
|
self.y_offset = 0
|
|
self.selected = -1
|
|
self.outline_thickness = 3
|
|
self.selected_colour = (255, 0, 0)
|
|
self.max_offset = 0
|
|
|
|
self.replace_list(texts)
|
|
self.release_function = self.check_click_pos
|
|
|
|
@property
|
|
def selected(self):
|
|
return self.__selected
|
|
|
|
@selected.setter
|
|
def selected(self, selected):
|
|
if selected < 0:
|
|
self.list_selected.emit(text='')
|
|
else:
|
|
self.list_selected.emit(text=self.texts[selected])
|
|
self.__selected = selected
|
|
|
|
def redraw(self):
|
|
super().redraw()
|
|
#if self.visible:
|
|
outline = (0, 0, self.rect.w, self.rect.h)
|
|
pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness)
|
|
i = 0
|
|
for text, text_rect in zip(self.texts, self.text_rects):
|
|
if i == self.selected:
|
|
pygame.draw.rect(self.background, self.selected_colour, text_rect)
|
|
rendered_text = self.font.render(text, True, self.text_colour).convert_alpha()
|
|
|
|
self.background.blit(rendered_text, text_rect)
|
|
i += 1
|
|
|
|
def process_events(self, event):
|
|
draw_update = super().process_events(event)
|
|
if event.type == pygame.MOUSEBUTTONUP:
|
|
mouse_pos = pygame.mouse.get_pos()
|
|
if self.collide_at(mouse_pos):
|
|
if event.button == 4:
|
|
self.scroll_up()
|
|
draw_update = True
|
|
if event.button == 5:
|
|
self.scroll_down()
|
|
draw_update = True
|
|
return draw_update
|
|
|
|
def offset_text_rects(self, offset):
|
|
prev_offset = self.y_offset
|
|
self.y_offset += offset
|
|
self.y_offset = max(-self.max_offset, self.y_offset)
|
|
self.y_offset = min(0, self.y_offset)
|
|
#if -self.max_offset <= self.y_offset <= 0:
|
|
for text_rect in self.text_rects:
|
|
text_rect.y += self.y_offset - prev_offset
|
|
|
|
def scroll_down(self, offset=10):
|
|
"""
|
|
To scroll down, all elements should shift up
|
|
:param offset:
|
|
:return:
|
|
"""
|
|
self.offset_text_rects(-offset)
|
|
self.redraw()
|
|
|
|
def scroll_up(self, offset=10):
|
|
self.offset_text_rects(offset)
|
|
self.redraw()
|
|
|
|
def check_click_pos(self, *args):
|
|
pos = args[0]
|
|
x0, y0 = self.get_offset_pos()
|
|
relative_pos_x = pos[0] - x0
|
|
relative_pos_y = pos[1] - y0
|
|
mouse_pos = (relative_pos_x, relative_pos_y)
|
|
for i, rect in enumerate(self.text_rects):
|
|
if rect.collidepoint(mouse_pos):
|
|
self.selected = i
|
|
self.redraw()
|
|
return
|
|
|
|
def reset_scroll(self):
|
|
self.offset_text_rects(-self.y_offset)
|
|
|
|
def add_item(self, text):
|
|
prev_offset = self.y_offset
|
|
self.reset_scroll()
|
|
self.texts.append(text)
|
|
current_y = self.text_rects[-1].y + self.text_rects[-1].height
|
|
rendered_text = self.font.render(text, True, self.text_colour).convert_alpha()
|
|
text_rect = rendered_text.get_rect()
|
|
text_rect.x = 0
|
|
text_rect.y = current_y
|
|
text_rect.width = self.width
|
|
self.text_rects.append(text_rect)
|
|
self.max_offset = max(0, self.max_offset+text_rect.height)
|
|
self.offset_text_rects(prev_offset)
|
|
self.scroll_down(text_rect.height)
|
|
self.redraw()
|
|
|
|
def remove_item(self, pos=-1):
|
|
prev_offset = self.y_offset
|
|
self.reset_scroll()
|
|
n_items = len(self.texts)
|
|
if self.texts and 0 <= pos < n_items:
|
|
self.texts.pop(pos)
|
|
text_rect = self.text_rects.pop(pos)
|
|
self.selected = min(self.selected, n_items-2)
|
|
if self.texts and pos < len(self.texts):
|
|
for rect in self.text_rects[pos:]:
|
|
rect.y -= text_rect.height
|
|
self.max_offset = max(0, self.max_offset-text_rect.height)
|
|
self.offset_text_rects(prev_offset)
|
|
self.scroll_up(text_rect.height)
|
|
self.redraw()
|
|
|
|
def replace_list(self, texts):
|
|
self.reset_scroll()
|
|
self.texts = texts
|
|
self.text_rects = []
|
|
current_y = self.outline_thickness
|
|
self.selected = -1
|
|
for text in texts:
|
|
rendered_text = self.font.render(text, True, self.text_colour).convert_alpha()
|
|
text_rect = rendered_text.get_rect()
|
|
text_rect.x = 0
|
|
text_rect.y = current_y
|
|
text_rect.width = self.width
|
|
self.text_rects.append(text_rect)
|
|
current_y += text_rect.height
|
|
self.max_offset = max(0, current_y - self.height)
|
|
self.redraw()
|
|
self.draw_update.emit()
|
|
|
|
|
|
class CallPanel(GenericUI):
|
|
"""
|
|
The panel to contain the UI for the player to make bids and call a partner.
|
|
"""
|
|
|
|
def __init__(self, x, y, width, height):
|
|
super().__init__(x, y, width, height)
|
|
self.background.set_colorkey(None)
|
|
self.confirm_output = Signal(args=['output'])
|
|
|
|
self.text_size = 20
|
|
margins = 5
|
|
ui_width = 80
|
|
ui_height = 25
|
|
width_spacings = (width - 2.5 * ui_width - 2 * margins) / 4
|
|
height_spacings = (height - 2 * margins - 3 * ui_height) / 4
|
|
self.output_text = ['', '']
|
|
|
|
self.list1 = ScrollList(margins+width_spacings, margins,
|
|
ui_width/2, height - 2*margins,
|
|
texts=[str(i) for i in range(20)], text_size=self.text_size)
|
|
self.list1.list_selected.connect(lambda text, **z: self.print_list_selection(text, 0))
|
|
|
|
self.list2 = ScrollList(margins+width_spacings*2+ui_width/2, margins,
|
|
ui_width, height - 2*margins,
|
|
texts=['a', 'b', 'c', 'd'], text_size=self.text_size)
|
|
self.list2.list_selected.connect(lambda text, **z: self.print_list_selection(text, 1))
|
|
|
|
self.output_box = TextBox(margins+width_spacings*3+ui_width*1.5, margins+height_spacings,
|
|
ui_width, ui_height, text='-', text_size=self.text_size)
|
|
|
|
self.confirm_button = Button(margins+width_spacings*3+ui_width*1.5, margins+height_spacings*2+ui_height,
|
|
ui_width, ui_height, text='Call', text_size=self.text_size)
|
|
self.confirm_button.clicked.connect(self.emit_output)
|
|
self.cancel_button = Button(margins + width_spacings * 3 + ui_width * 1.5,
|
|
margins + height_spacings * 3 + ui_height * 2,
|
|
ui_width, ui_height, text='Pass', text_size=self.text_size)
|
|
|
|
self.cancel_button.visible = False
|
|
self.cancel_button.clicked.connect(self.cancelling)
|
|
#self.children = [self.label1, self.list1, self.label2, self.list2,
|
|
self.children = [self.list1, self.list2,
|
|
self.confirm_button, self.output_box, self.cancel_button]
|
|
for element in self.children:
|
|
element.parent = self
|
|
|
|
self.redraw()
|
|
|
|
def redraw(self, **kwargs):
|
|
super().redraw()
|
|
#self.background.fill((255,0,255))
|
|
#if self.visible:
|
|
outline = (0, 0, self.rect.w, self.rect.h)
|
|
pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness)
|
|
|
|
for element in self.children:
|
|
if element.visible:
|
|
self.background.blit(element.background, element.get_pos())
|
|
|
|
def process_events(self, event):
|
|
draw_update = False
|
|
for element in self.children:
|
|
if element.visible and element.process_events(event):
|
|
draw_update = True
|
|
|
|
if draw_update:
|
|
self.redraw()
|
|
return draw_update
|
|
|
|
def print_list_selection(self, text, num,**kwargs):
|
|
self.output_text[num] = text
|
|
self.output_box.set_text(' '.join(self.output_text))
|
|
|
|
def emit_output(self, **kwargs):
|
|
initial = ''
|
|
if self.output_text[1]:
|
|
initial = self.output_text[1][0].lower()
|
|
|
|
output = self.output_text[0].lower() + initial
|
|
self.confirm_output.emit(output=output)
|
|
|
|
def cancelling(self, **kwargs):
|
|
self.confirm_output.emit(output='')
|
|
|
|
def change_lists_elements(self, left_list=None, right_list=None):
|
|
if left_list is not None:
|
|
self.list1.replace_list(left_list)
|
|
if right_list is not None:
|
|
self.list2.replace_list(right_list)
|
|
self.redraw()
|
|
|
|
|
|
class TestScreen(view.PygView):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
texts = [str(i) for i in range(20)]
|
|
|
|
self.scroll_menu = ScrollList(100, 100, 100, 200, texts=texts)
|
|
self.button = Button(300, 100, 50, 25, text_size=18)
|
|
self.textbox = TextBox(300, 250, 200, 100, text="Test")
|
|
self.panel = CallPanel(100, 100, 300, 150)
|
|
#self.panel.confirm_output.connect(self.print_panel_output)
|
|
|
|
#[self.scroll_menu, self.button, self.textbox]
|
|
self.elements = [self.panel]
|
|
|
|
self.double_clicking = False
|
|
self.left_mouse_down = False
|
|
self.double_click_event = pygame.USEREVENT + 1
|
|
|
|
self.draw_function()
|
|
|
|
pygame.display.flip()
|
|
self.screen.blit(self.background, (0, 0))
|
|
|
|
def draw_function(self):
|
|
for element in self.elements:
|
|
self.screen.blit(element.background, element.get_pos())
|
|
self.screen.blit(element.background, element.get_pos())
|
|
|
|
#def print_panel_output(self, output, **kwargs):
|
|
# print(output)
|
|
|
|
def run(self):
|
|
running = True
|
|
while running:
|
|
draw_update = False
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.QUIT:
|
|
running = False
|
|
elif event.type == pygame.KEYDOWN:
|
|
if event.key == pygame.K_ESCAPE:
|
|
running = False
|
|
|
|
if event.key == pygame.K_o:
|
|
self.panel.change_lists_elements([str(i+1) for i in range(45)],['a','b'])
|
|
draw_update = True
|
|
|
|
for element in self.elements:
|
|
if element.process_events(event):
|
|
draw_update = True
|
|
|
|
if event.type == pygame.MOUSEBUTTONUP:
|
|
if event.button == 1:
|
|
print('mouse click')
|
|
if self.double_clicking:
|
|
pygame.time.set_timer(self.double_click_event, 0)
|
|
print('Double clicked')
|
|
self.double_clicking = False
|
|
else:
|
|
self.double_clicking = True
|
|
pygame.time.set_timer(self.double_click_event, 200)
|
|
|
|
if event.type == self.double_click_event:
|
|
pygame.time.set_timer(self.double_click_event, 0)
|
|
self.double_clicking = False
|
|
print('double click disabled')
|
|
|
|
if draw_update:
|
|
self.draw_function()
|
|
|
|
pygame.display.flip()
|
|
self.screen.blit(self.background, (0, 0))
|
|
|
|
pygame.quit()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
test_view = TestScreen(640, 400, clear_colour=(0, 0, 0))
|
|
test_view.run()
|