From: Stan_Lewry Date: Fri, 23 Jul 2021 13:54:01 +0000 (+0100) Subject: Implemented new basic button widget with simple sprite. A button can be hovered over... X-Git-Url: https://stanlewry.com/index.cgi?a=commitdiff_plain;h=c8c6656635637a1c538138129f056d17aac10175;p=pyroguelike.git Implemented new basic button widget with simple sprite. A button can be hovered over and clicked and an event string can be specified to be emitted when it is clicked. Handled simple quit event in main to demonstrate. Events are queued by the button and popped in main where they are handled. Eventually this can be moved to the Game class or whatever. Could create something where other classes can subscribe to certain events or something. Can specify position, font, text, colour, and the event to emit on click in the JSON. Re worked fonts in Texture.py to support multiple fonts at multiple sizes. Changes to InputHandler to support mouse events. screenshots for writeup. --- diff --git a/Assets/GUIScenes/TestScene.json b/Assets/GUIScenes/TestScene.json index 76c1329..dba393d 100644 --- a/Assets/GUIScenes/TestScene.json +++ b/Assets/GUIScenes/TestScene.json @@ -1,23 +1,51 @@ { "widgets": [ { - "type" : "ImageWidget", + "type" : "BasicButton", + "props" : { + "x" : 1000, + "y" : 600, + "w" : 256, + "h" : 64, + "event" : "quit game", + "text" : "Exit", + "font" : "m12", + "fontSize" : "medium", + "colour" : "255, 255, 255" + } + }, + { + "type" : "BasicButton", + "props" : { + "x" : 1000, + "y" : 664, + "w" : 256, + "h" : 64, + "event" : "", + "text" : "Test", + "font" : "m12", + "fontSize" : "medium", + "colour" : "255, 255, 255" + } + }, + { + "type" : "Image", "props" : { "source" : "smile.png", - "x" : 100, - "y" : 100, - "w" : 400, - "h" : 500, + "x" : 30, + "y" : 60, + "w" : 128, + "h" : 128, "fit" : true } }, { - "type": "ImageWidget", + "type": "Image", "props" : { - "w" : 250, - "h" : 250, - "x" : 1200, - "y" : 600, + "w" : 200, + "h" : 200, + "x" : 1700, + "y" : 800, "source" : "test.png", "fit" : true } diff --git a/Assets/basicButton.png b/Assets/basicButton.png new file mode 100644 index 0000000..dc52507 Binary files /dev/null and b/Assets/basicButton.png differ diff --git a/GUI/BasicButtonWidget.py b/GUI/BasicButtonWidget.py new file mode 100644 index 0000000..d8531da --- /dev/null +++ b/GUI/BasicButtonWidget.py @@ -0,0 +1,61 @@ +import GUI.Widget as Widget +import Texture +from enum import Enum +import pygame +import os + +BUTTONSTATE_IDLE = 0 +BUTTONSTATE_HOVER = 1 +BUTTONSTATE_CLICK = 2 + +class BasicButtonWidget(Widget.Widget): + def __init__(self, gui, x, y, w, h, event, text, font, fontSize, colour) -> None: + super().__init__(gui) + + self.event = event + + self.screenX = x + self.screenY = y + self.screenW = w + self.screenH = h + + self.text = text + self.font = font + self.fontSize = fontSize + self.colour = colour + + self.surface = pygame.Surface((self.screenW, self.screenH)) + self.buttonImage = pygame.image.load(os.path.join('Assets', 'basicButton.png')) + self.buttonImage = pygame.transform.scale(self.buttonImage, (self.screenW * 3, self.screenH)) + self.setSurface(BUTTONSTATE_IDLE) + + def setHover(self, hover) -> None: + if not hover == self.hover: + self.hover = hover + if self.hover: + self.setSurface(BUTTONSTATE_HOVER) + else: + self.setSurface(BUTTONSTATE_IDLE) + + def setClicked(self, click) -> None: + if not click == self.click: + self.click = click + self.hover = False + if self.click: + self.setSurface(BUTTONSTATE_CLICK) + self._onClicked() + else: + self.setSurface(BUTTONSTATE_IDLE) + + def setSurface(self, buttonState) -> None: + sourceRect = (buttonState * self.screenW, 0, self.screenW, self.screenH) + self.surface.blit(self.buttonImage, (0,0), sourceRect) + text = Texture.fontDict[self.textSize].render(self.text, False, self.textCol) + textRect = text.get_rect() + textOriginX = (self.screenW / 2) - (textRect.w / 2) + textOriginY = (self.screenH / 2) - (textRect.h / 2) + self.surface.blit(text, (textOriginX, textOriginY)) + + def _onClicked(self) -> None: + self._emit(self.event) + pass \ No newline at end of file diff --git a/GUI/GuiManager.py b/GUI/GuiManager.py index 848f967..d3593cc 100644 --- a/GUI/GuiManager.py +++ b/GUI/GuiManager.py @@ -1,6 +1,7 @@ import json from dataclasses import dataclass import GUI.ImageWidget as ImageWidget +import GUI.BasicButtonWidget as BasicButtonWidget @dataclass class Event: @@ -8,6 +9,7 @@ class Event: class GuiManager: def __init__(self) -> None: + # maybe make this a dictionary so they can have ids :) self.widgetList = [] self.eventList = [] @@ -19,7 +21,7 @@ class GuiManager: with open(path, "r") as file: data = json.loads(file.read()) for widget in data["widgets"]: - if widget["type"] == "ImageWidget" : + if widget["type"] == "Image" : props = widget["props"] self.createImageWidget( props["x"], @@ -29,14 +31,41 @@ class GuiManager: props["source"], props["fit"] ) + elif widget["type"] == "BasicButton" : + props = widget["props"] + self.createBasicButtonWidget( + props["x"], + props["y"], + props["w"], + props["h"], + props["event"], + props["text"], + props["font"], + props["fontSize"], + tuple(map(int, props["colour"].split(', '))) + ) + def createImageWidget(self, x, y, w, h, path, fit) -> None: self.widgetList.append(ImageWidget.ImageWidget(self, x, y, w, h, path, fit)) - def mouseClicked(self, mouseX, mouseY) -> None: - # look through all widgets and see if the mouse hit it, if so call it's - # onClicked function - pass + def createBasicButtonWidget(self, x, y, w, h, event, text, font, fontSize, colour) -> None: + self.widgetList.append(BasicButtonWidget.BasicButtonWidget(self, x, y, w, h, event, text, font, fontSize, colour)) + + def handleMouseEvent(self, mouseX, mouseY, mouseDown) -> None: + for widget in self.widgetList: + if (mouseX > widget.screenX + and mouseX < widget.screenX + widget.screenW + and mouseY > widget.screenY + and mouseY < widget.screenY + widget.screenH): + if (mouseDown): + widget.setClicked(True) + else: + widget.setClicked(False) + widget.setHover(True) + else: + widget.setHover(False) + def registerEvent(self, event) -> None: self.eventList.append(event) \ No newline at end of file diff --git a/GUI/Widget.py b/GUI/Widget.py index 96f227c..41451b5 100644 --- a/GUI/Widget.py +++ b/GUI/Widget.py @@ -12,9 +12,17 @@ class Widget: self.screenY = 0 self.width = 0 self.height = 0 + self.hover = False + self.click = False - def onClicked(self) -> None: + def setClicked(self, click) -> None: pass - def emit(self, emitString) -> None: - self.guiManager.registerEvent(GuiManager.Event(emitString)) \ No newline at end of file + def setHover(self, hover) -> None: + pass + + def _emit(self, emitString) -> None: + self.guiManager.registerEvent(GuiManager.Event(emitString)) + + def _onClicked(self) -> None: + pass \ No newline at end of file diff --git a/InputHandler.py b/InputHandler.py index 5d51aff..e755f1d 100644 --- a/InputHandler.py +++ b/InputHandler.py @@ -11,6 +11,8 @@ class InputState: debugLevel: int = 0 inputMade: bool = False debugRegenWorld: bool = False + mousePos: Tuple = (0, 0) + lmbDown: bool = False maxZoom = 3.0 minZoom = 0.125 @@ -47,7 +49,13 @@ def handleInputs(): else: newInputState.debugLevel = 0 elif event.key == pygame.K_F4: newInputState.debugRegenWorld = True + elif event.type == pygame.MOUSEBUTTONDOWN: + newInputState.lmbDown = True + elif event.type == pygame.MOUSEBUTTONUP: + newInputState.lmbDown = False + newInputState.mousePos = pygame.mouse.get_pos() + newInputState.inputMade = not newInputState == globalInputState # now replace the global one with our new one # mutex lock if I move to thread diff --git a/Main.py b/Main.py index f672bfe..5096b22 100644 --- a/Main.py +++ b/Main.py @@ -23,11 +23,13 @@ def main(): player = Player.Player(World.worldWidth / 2, World.worldHeight / 2) + running = True + # begin game loop getTicksLastFrame = 0 t = 0 deltaTime = 0 - while not InputHandler.globalInputState.quit: + while running: # Compute delta time t = pygame.time.get_ticks() deltaTime = (t - getTicksLastFrame) / 1000.0 @@ -36,6 +38,15 @@ def main(): Debug.debugRects.clear() InputHandler.handleInputs() + running = not InputHandler.globalInputState.quit + + guiManager.handleMouseEvent(*InputHandler.globalInputState.mousePos, InputHandler.globalInputState.lmbDown) + + # handle event queue + while guiManager.eventList: + event = guiManager.eventList.pop() + if event.event == "quit game": + running = False player.update() diff --git a/Texture.py b/Texture.py index c2a2bf4..a9fddc1 100644 --- a/Texture.py +++ b/Texture.py @@ -7,6 +7,20 @@ spriteSheet = pygame.image.load(os.path.join('Assets', 'doomSheet.png')) animationSheet = pygame.image.load(os.path.join('Assets', 'animation.png')) font = pygame.font.Font(os.path.join('Assets','m12.ttf'), 28) +#smallFont = pygame.font.Font(os.path.join('Assets', 'm12.ttf'), 12) +# = pygame.font.Font(os.path.join('Assets', 'm12.ttf'), 12) + +m12FontDict = { + "small" : pygame.font.Font(os.path.join('Assets', 'm12.ttf'), 12), + "medium" : pygame.font.Font(os.path.join('Assets', 'm12.ttf'), 20), + "large" : pygame.font.Font(os.path.join('Assets', 'm12.ttf'), 28), + "veryLarge" : pygame.font.Font(os.path.join('Assets', 'm12.ttf'), 32) +} + +fontDictDict = { + "m12" : m12FontDict +} + sheetWidth = spriteSheet.get_width() sheetHeight = spriteSheet.get_height() # multiply coords by sprite size :) diff --git a/Writeup/guitest.png b/Writeup/guitest.png new file mode 100644 index 0000000..aa808c3 Binary files /dev/null and b/Writeup/guitest.png differ diff --git a/Writeup/guitest2.png b/Writeup/guitest2.png new file mode 100644 index 0000000..076583d Binary files /dev/null and b/Writeup/guitest2.png differ