From c181b46af8e575d4871daf8e635087e5fea1a17a Mon Sep 17 00:00:00 2001 From: Wally Hackenslacker Date: Sun, 5 Oct 2025 22:09:20 -0400 Subject: [PATCH] Added text buttons and button groups. --- README.md | 5 +- assets/imgs/concb03.png | Bin 0 -> 31945 bytes main.lua | 6 +-- src/graphics/drawable.lua | 6 +-- src/graphics/sprite.lua | 24 ++++++--- src/gstates/menu.lua | 42 +++++++++++----- src/ui/btngrp.lua | 100 ++++++++++++++++++++++++++++++++++++++ src/ui/button.lua | 89 +++++++++++++++++++++++++++++++++ src/ui/font.lua | 19 +++++++- src/ui/textbtn.lua | 73 ++++++++++++++++++++++++++++ src/utils/asset.lua | 5 ++ src/utils/asstmngr.lua | 2 +- src/utils/classes.lua | 18 +++---- src/utils/colls.lua | 17 +++++++ src/utils/color.lua | 30 ++++++++++++ 15 files changed, 398 insertions(+), 38 deletions(-) create mode 100644 assets/imgs/concb03.png create mode 100644 src/ui/btngrp.lua create mode 100644 src/ui/button.lua create mode 100644 src/ui/textbtn.lua create mode 100644 src/utils/colls.lua create mode 100644 src/utils/color.lua diff --git a/README.md b/README.md index f5f5e57..e0e65ca 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,12 @@ A dungeon crawler game made with LoveDOS for the DOSember Game Jam https://itch. ### Sprites - [B&W Ornamental Cursor by qubodup](https://opengameart.org/content/bw-ornamental-cursor-19x19) -- [Microchip texture by ZeptoBARS](https://opengameart.org/content/microchip-texture) - [Eris in colour by Stephen](https://openclipart.org/detail/329762/eris-in-colour) +### Textures +- [Microchip texture by ZeptoBARS](https://opengameart.org/content/microchip-texture) +- [Assorted textures by Makkon](https://www.slipseer.com/index.php?resources/makkon-textures.28/) + ### Sound effects - [Click.wav by frosty ham](https://opengameart.org/content/click-0) - [Sci-Fi Sound Effects Library by Little Robot Sound Factory](https://opengameart.org/content/sci-fi-sound-effects-library) diff --git a/assets/imgs/concb03.png b/assets/imgs/concb03.png new file mode 100644 index 0000000000000000000000000000000000000000..6f6dd075d92b3de9782644d2d232afd8f5ed712d GIT binary patch literal 31945 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 z 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 1 and (self.btns[i - 1].h + self.s)) or 0) + + v.x = self.x + v.y = y + end +end + + +function ButtonGroup:unload() + for _, v in pairs(self.btns) do + if v.is_a[Asset] then + v:unload() + end + end +end + + +function ButtonGroup:update(dt) + for _, v in pairs(self.btns) do + v:update(dt) + end +end + + +function ButtonGroup:draw() + for _, v in pairs(self.btns) do + v:draw() + end +end + + +function ButtonGroup:mousemoved(x, y, dx, dy) + for _, v in pairs(self.btns) do + v:mousemoved(x, y, dx, dy) + end +end + + +function ButtonGroup:mousepressed(x, y, btn) + for _, v in pairs(self.btns) do + v:mousepressed(x, y, btn) + end +end + + +function ButtonGroup:mousereleased(x, y, btn) + for _, v in pairs(self.btns) do + v:mousereleased(x, y, btn) + end +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return ButtonGroup diff --git a/src/ui/button.lua b/src/ui/button.lua new file mode 100644 index 0000000..e31b88b --- /dev/null +++ b/src/ui/button.lua @@ -0,0 +1,89 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local make_class = require 'src.utils.classes' +local collisions = require 'src.utils.colls' +local Asset = require 'src.utils.asset' +local Drawable = require 'src.graphics.drawable' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local Button = make_class(Drawable, Asset) + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function Button:_init(x, y, callback) + self.x = x + self.y = y + self.w = nil + self.h = nil + self.selected = false + self.pressed = false + self.was_pressed = false + self.callback = callback +end + + +function Button:set_dimensions() + self.w = 0 + self.h = 0 +end + + +function Button:mousemoved(x, y) + -- Select if the pointer is inside the button's bounding rect. + self.selected = collisions.point_in_square(x, y, self.x, self.y, self.w, self.h) + + -- If the button was pressed then check if the pointer is no longer inside the button. + if self.pressed then + -- If it's not, then mark the button as "was pressed". + self.pressed = collisions.point_in_square(x, y, self.x, self.y, self.w, self.h) + self.was_pressed = true + end + + -- If the button was pressed and the pointer is inside the button right now. + if self.was_pressed and collisions.point_in_square(x, y, self.x, self.y, self.w, self.h) then + -- Then mark the button as pressed again. + self.pressed = true + self.was_pressed = false + end +end + + +function Button:mousepressed(x, y, btn) + -- If the pointer was inside the button when it was pressed then mark the button as pressed. + if btn == 0 then + self.pressed = collisions.point_in_square(x, y, self.x, self.y, self.w, self.h) + end +end + + +function Button:mousereleased(x, y, btn) + if btn == 0 then + if self.pressed then + -- If the button was pressed then execute the callback if any and unmark as pressed. + if self.callback ~= nil then + self.callback() + end + + self.pressed = false + end + + -- The button no longer was pressed. + self.was_pressed = false + end +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return Button diff --git a/src/ui/font.lua b/src/ui/font.lua index bc2d95a..1472405 100644 --- a/src/ui/font.lua +++ b/src/ui/font.lua @@ -27,7 +27,9 @@ end function Font:load() - self.f = love.graphics.newFont(self.file_name, self.size) + if not self:is_loaded() then + self.f = love.graphics.newFont(self.file_name, self.size) + end end @@ -36,6 +38,11 @@ function Font:unload() end +function Font:is_loaded() + return self.f ~= nil +end + + function Font:set() if self.f ~= nil then love.graphics.setFont(self.f) end end @@ -46,6 +53,16 @@ function Font:unset() end +function Font:get_width(text) + if self.f ~= nil then return self.f:getWidth(text) else return nil end +end + + +function Font:get_height(text) + if self.f ~= nil then return self.f:getHeight(text) else return nil end +end + + ------------------------------------------------------------------------------ -- Module return ------------------------------------------------------------------------------ diff --git a/src/ui/textbtn.lua b/src/ui/textbtn.lua new file mode 100644 index 0000000..6019efa --- /dev/null +++ b/src/ui/textbtn.lua @@ -0,0 +1,73 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local love = require 'love' +local make_class = require 'src.utils.classes' +local Button = require 'src.ui.button' +local Color = require 'src.utils.color' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local TextButton = make_class(Button) + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function TextButton:_init(text, font, x, y, callback, base_col, sel_color, press_col) + Button._init(self, x, y, callback) + self.font = font + self.text = text + self.base_col = base_col ~= nil and base_col or Color(255, 255, 255) + self.sel_color = sel_color ~= nil and sel_color or Color(215, 0, 0) + self.press_col = press_col ~= nil and press_col or Color(99, 99, 139) +end + + +function TextButton:load() + if not self.font:is_loaded() then + self.font:load() + end + + -- If the label's size has not been computed then do it. + if self.w == nil or self.h == nil then self:set_dimensions() end +end + + +function TextButton:unload() + self.font:unload() +end + + +function TextButton:is_loaded() + return self.font:is_loaded() and self.w ~= nil and self.h ~= nil +end + + +function TextButton:set_dimensions() + self.w = self.font:get_width(self.text) + self.h = self.font:get_height(self.text) +end + + +function TextButton:draw() + local color = (self.pressed and self.press_col) or ((self.selected and self.sel_color) or self.base_col) + + love.graphics.setColor(color.r, color.g, color.b) + self.font:set() + love.graphics.print(self.text, self.x, self.y) + self.font:unset() + love.graphics.setColor() +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return TextButton diff --git a/src/utils/asset.lua b/src/utils/asset.lua index cfa74c2..5792bf6 100644 --- a/src/utils/asset.lua +++ b/src/utils/asset.lua @@ -31,6 +31,11 @@ function Asset:unload() end +function Asset:is_loaded() + return false +end + + ------------------------------------------------------------------------------ -- Module return ------------------------------------------------------------------------------ diff --git a/src/utils/asstmngr.lua b/src/utils/asstmngr.lua index b53884a..4e7ec3e 100644 --- a/src/utils/asstmngr.lua +++ b/src/utils/asstmngr.lua @@ -42,7 +42,7 @@ function AssetManager:_init() Drawable._init(self) self.assets = {} self.bckg = Sprite('imgs/floppy.png') - self.font = Font('fonts/Concrete.ttf', 40) + self.font = Font('fonts/BBrick.ttf', 40) self.co = nil self.bckg:load() diff --git a/src/utils/classes.lua b/src/utils/classes.lua index 760e2c6..f302812 100644 --- a/src/utils/classes.lua +++ b/src/utils/classes.lua @@ -2,15 +2,14 @@ -- Methods ------------------------------------------------------------------------------ --- Single inheritance version of the sample code from --- http://lua-users.org/wiki/ObjectOrientationTutorial -local function make_class(BaseClass) +-- Code from http://lua-users.org/wiki/ObjectOrientationTutorial +local function make_class(...) -- "cls" is the new class - local cls = {} + local cls, bases = {}, {...} -- copy base class contents into the new class - if BaseClass ~= nil then - for k, v in pairs(BaseClass) do + for _, base in ipairs(bases) do + for k, v in pairs(base) do cls[k] = v end end @@ -19,10 +18,11 @@ local function make_class(BaseClass) -- so you can do an "instance of" check using my_instance.is_a[MyClass] cls.__index, cls.is_a = cls, {[cls] = true} - if BaseClass ~= nil then - for k, _ in pairs(BaseClass.is_a) do - cls.is_a[k] = true + for _, base in ipairs(bases) do + for c in pairs(base.is_a) do + cls.is_a[c] = true end + cls.is_a[base] = true end -- the class's __call metamethod diff --git a/src/utils/colls.lua b/src/utils/colls.lua new file mode 100644 index 0000000..0c61605 --- /dev/null +++ b/src/utils/colls.lua @@ -0,0 +1,17 @@ +------------------------------------------------------------------------------ +-- Module definitions +------------------------------------------------------------------------------ + +local collisions = { + -- Default setting values. + point_in_square = function(x, y, ox, oy, w, h) + return x >= ox and x <= ox + w and y >= oy and y <= oy + h + end, +} + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return collisions diff --git a/src/utils/color.lua b/src/utils/color.lua new file mode 100644 index 0000000..9843f30 --- /dev/null +++ b/src/utils/color.lua @@ -0,0 +1,30 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local make_class = require 'src.utils.classes' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local Color = make_class() + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function Color:_init(r, g, b) + self.r = r ~= nil and r or 255 + self.g = g ~= nil and g or 255 + self.b = b ~= nil and b or 255 +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return Color