From 777b70d1ab40702579cc14b0cf31b0a183ca1aa2 Mon Sep 17 00:00:00 2001 From: Wally Hackenslacker Date: Mon, 13 Oct 2025 00:28:12 -0400 Subject: [PATCH] Added dialog windows and control settings. Lots of comments and changes in UI code in general. --- assets/imgs/tchcmp1.png | Bin 0 -> 25098 bytes main.lua | 15 +++-- src/graphics/drawable.lua | 1 + src/gstates/menu.lua | 79 ++++++++++++++++++----- src/gstates/menus/options.lua | 96 ++++++++++++++++++++++++--- src/ui/bar.lua | 2 +- src/ui/button.lua | 7 +- src/ui/dialog.lua | 117 +++++++++++++++++++++++++++++++++ src/ui/dlgmngr.lua | 104 ++++++++++++++++++++++++++++++ src/ui/element.lua | 33 +++++++++- src/ui/font.lua | 3 + src/ui/hbox.lua | 5 +- src/ui/label.lua | 13 ++-- src/ui/layout.lua | 24 ++++++- src/ui/textinpt.lua | 118 ++++++++++++++++++++++++++++++++++ src/ui/vbox.lua | 5 +- src/utils/asset.lua | 1 + src/utils/classes.lua | 4 +- src/utils/color.lua | 1 + src/utils/settings.lua | 41 ++++++------ 20 files changed, 607 insertions(+), 62 deletions(-) create mode 100644 assets/imgs/tchcmp1.png create mode 100644 src/ui/dialog.lua create mode 100644 src/ui/dlgmngr.lua create mode 100644 src/ui/textinpt.lua diff --git a/assets/imgs/tchcmp1.png b/assets/imgs/tchcmp1.png new file mode 100644 index 0000000000000000000000000000000000000000..532a89abf70e9bbc699ddeed5269bfdaa9f22867 GIT binary patch literal 25098 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 literal 0 HcmV?d00001 diff --git a/main.lua b/main.lua index 5e03888..bbf3626 100644 --- a/main.lua +++ b/main.lua @@ -2,10 +2,10 @@ -- Imports ------------------------------------------------------------------------------ -local love = require 'love' -local game_states = require 'src.states' -local settings = require 'src.utils.settings' -local Fader = require 'src.graphics.fader' +local love = require 'love' +local game_states = require 'src.states' +local settings = require 'src.utils.settings' +local Fader = require 'src.graphics.fader' ------------------------------------------------------------------------------ @@ -87,13 +87,14 @@ function love.draw() Fade:draw() if Debug then - love.graphics.setColor(0, 0, 0) - love.graphics.rectangle('fill', 0, 0, 140, 50) - love.graphics.setColor() + -- love.graphics.setColor(0, 0, 0) + -- love.graphics.rectangle('fill', 0, 0, 140, 50) + love.graphics.setColor(255, 250, 0) love.graphics.print(string.format('OS: %s', love.system.getOS()), 5, 5) love.graphics.print(string.format('Love version: %s', love.getVersion()), 5, 15) love.graphics.print(string.format('Memory usage: %s KiB', love.system.getMemUsage()), 5, 25) love.graphics.print(string.format('FPS: %.2f', 1.0 / love.timer.getAverageDelta()), 5, 35) + love.graphics.setColor() end end diff --git a/src/graphics/drawable.lua b/src/graphics/drawable.lua index 86c989b..c835a14 100644 --- a/src/graphics/drawable.lua +++ b/src/graphics/drawable.lua @@ -8,6 +8,7 @@ local make_class = require 'src.utils.classes' -- Class definitions ------------------------------------------------------------------------------ +---@class Drawable local Drawable = make_class() diff --git a/src/gstates/menu.lua b/src/gstates/menu.lua index 91c245c..04d0e46 100644 --- a/src/gstates/menu.lua +++ b/src/gstates/menu.lua @@ -2,18 +2,19 @@ -- Imports ------------------------------------------------------------------------------ -local love = require 'love' -local assets = require 'src.utils.asstmngr' -local sound_manager = require 'src.sound.sndmngr' -local make_class = require 'src.utils.classes' -local constants = require 'src.gstates.menus.const' -local GameState = require 'src.gstates.gstate' -local MainMenu = require 'src.gstates.menus.mainmenu' -local OptionsMenu = require 'src.gstates.menus.options' -local Fader = require 'src.graphics.fader' -local Cursor = require 'src.ui.cursor' -local Font = require 'src.ui.font' -local SoundEffect = require 'src.sound.sfx' +local love = require 'love' +local assets = require 'src.utils.asstmngr' +local sound_manager = require 'src.sound.sndmngr' +local dialog_manager = require 'src.ui.dlgmngr' +local make_class = require 'src.utils.classes' +local constants = require 'src.gstates.menus.const' +local GameState = require 'src.gstates.gstate' +local MainMenu = require 'src.gstates.menus.mainmenu' +local OptionsMenu = require 'src.gstates.menus.options' +local Fader = require 'src.graphics.fader' +local Cursor = require 'src.ui.cursor' +local Font = require 'src.ui.font' +local SoundEffect = require 'src.sound.sfx' ------------------------------------------------------------------------------ @@ -63,6 +64,7 @@ function Menu:update(dt) if self.all_loaded then self.menus[self.current_menu]:update(dt) + dialog_manager:update(dt) -- If the game state changed then trigger a fade out. if self.next_menu ~= self.current_menu and self.fade.done then @@ -101,6 +103,7 @@ end function Menu:draw() if self.all_loaded then self.menus[self.current_menu]:draw() + dialog_manager:draw() self.cursor:draw() self.fade:draw() else @@ -108,23 +111,69 @@ function Menu:draw() end end +function Menu:keypressed(key, code, isrepeat) + -- Send events to the active game state if there is no fade active. + if Fade.done then + if dialog_manager:empty() then + self.menus[self.current_menu]:keypressed(key, code, isrepeat) + else + dialog_manager:keypressed(key, code, isrepeat) + end + end +end + + +function Menu:keyreleased(key, code) + -- Send events to the active game state if there is no fade active. + if Fade.done then + if dialog_manager:empty() then + self.menus[self.current_menu]:keyreleased(key, code) + else + dialog_manager:keyreleased(key, code) + end + end +end + + +function Menu:textinput(text) + -- Send events to the active game state if there is no fade active. + if Fade.done then + if dialog_manager:empty() then + self.menus[self.current_menu]:textinput(text) + else + dialog_manager:textinput(text) + end + end +end function Menu:mousemoved(x, y, dx, dy) - self.menus[self.current_menu]:mousemoved(x, y, dx, dy) + if dialog_manager:empty() then + self.menus[self.current_menu]:mousemoved(x, y, dx, dy) + else + dialog_manager:mousemoved(x, y, dx, dy) + end self.cursor:mousemoved(x, y, dx, dy) end function Menu:mousepressed(x, y, btn) if self.fade.done then - self.menus[self.current_menu]:mousepressed(x, y, btn) + if dialog_manager:empty() then + self.menus[self.current_menu]:mousepressed(x, y, btn) + else + dialog_manager:mousepressed(x, y, btn) + end end end function Menu:mousereleased(x, y, btn) if self.fade.done then - self.menus[self.current_menu]:mousereleased(x, y, btn) + if dialog_manager:empty() then + self.menus[self.current_menu]:mousereleased(x, y, btn) + else + dialog_manager:mousereleased(x, y, btn) + end end end diff --git a/src/gstates/menus/options.lua b/src/gstates/menus/options.lua index 1b15a67..562d6aa 100644 --- a/src/gstates/menus/options.lua +++ b/src/gstates/menus/options.lua @@ -6,13 +6,17 @@ local make_class = require 'src.utils.classes' local constants = require 'src.gstates.menus.const' local settings = require 'src.utils.settings' local sound_manager = require 'src.sound.sndmngr' +local assets = require 'src.utils.asstmngr' local BaseMenu = require 'src.gstates.menus.base' local Color = require 'src.utils.color' local HBox = require 'src.ui.hbox' local Label = require 'src.ui.label' local TextButton = require 'src.ui.textbtn' +local TextInput = require 'src.ui.textinpt' local Checkbox = require 'src.ui.chkbox' local Bar = require 'src.ui.bar' +local Dialog = require 'src.ui.dialog' +local Font = require 'src.ui.font' ------------------------------------------------------------------------------ @@ -29,11 +33,15 @@ local OptionsMenu = make_class(BaseMenu) function OptionsMenu:_init(parent, title_font, button_font) BaseMenu._init(self, parent, "Options Menu", constants.OPTIONS_MENU, 'imgs/cpu.png') + self.subtitle_font = Font('fonts/BBrick.ttf', 25) + -- Create UI elements of the menu. self.container:add(Label(nil, nil, 'Options', title_font, Color(215, 0, 0), true)) + -- Permadeath checkbox. local box = HBox() - box:add(Label(nil, nil, 'Enable Permadeath', button_font)) + + box:add(Label(nil, nil, 'Enable Permadeath: ', button_font)) box:add(Checkbox( function() settings.permadeath = not settings.permadeath @@ -43,14 +51,77 @@ function OptionsMenu:_init(parent, title_font, button_font) end, 17, 15)) + self.container:add(box) - box = HBox() - box:add(Label(nil, nil, 'Controls', button_font)) - self.container:add(box) + -- Control binding dialog. + local dlg = Dialog('imgs/tchcmp1.png', nil, 15, nil, nil, button_font) + + dlg:add(Label(nil, nil, 'Set Controls', self.subtitle_font, Color(215, 0, 0), true)) box = HBox() - box:add(Label(nil, nil, 'Sound Volume', button_font)) + box.spacing = 30 + -- text, font, x, y, callback, value, float, base_col, sel_color, press_col + box:add(TextInput('Step Forw.', + button_font, + nil, nil, + function(v) settings.forward = v end, + function() return settings.forward end)) + box:add(TextInput('Step Back', + button_font, + nil, nil, + function(v) settings.backward = v end, + function() return settings.backward end)) + + dlg:add(box) + + box = HBox() + box.spacing = 40 + box:add(TextInput('Step Left', + button_font, + nil, nil, + function(v) settings.stepleft = v end, + function() return settings.stepleft end)) + box:add(TextInput('Step Right', + button_font, + nil, nil, + function(v) settings.stepright = v end, + function() return settings.stepright end)) + + dlg:add(box) + + box = HBox() + box.spacing = 42 + box:add(TextInput('Turn Left', + button_font, + nil, nil, + function(v) settings.turnleft = v end, + function() return settings.turnleft end)) + box:add(TextInput('Turn Right', + button_font, + nil, nil, + function(v) settings.turnright = v end, + function() return settings.turnright end)) + + dlg:add(box) + + box = HBox() + + box:add(TextButton( + 'Set Controls', + button_font, + nil, + nil, + function() + dlg:show() + end)) + + self.container:add(box) + + -- Sound volume bar. + box = HBox() + + box:add(Label(nil, nil, 'Sound Volume: ', button_font)) box:add(Bar( function(v) settings.soundvol = math.ceil(v) @@ -59,12 +130,15 @@ function OptionsMenu:_init(parent, title_font, button_font) return settings.soundvol end, 0, 100, - 175, 15)) + 170, 15)) + self.container:add(box) + -- Music volume bar. box = HBox() box.spacing = 13 - box:add(Label(nil, nil, 'Music Volume', button_font)) + + box:add(Label(nil, nil, 'Music Volume: ', button_font)) box:add(Bar( function(v) settings.musicvol = math.ceil(v) @@ -74,9 +148,11 @@ function OptionsMenu:_init(parent, title_font, button_font) return settings.musicvol end, 0, 100, - 175, 15)) + 170, 15)) + self.container:add(box) + -- Back button. box = HBox() box:add(TextButton( 'Go Back', @@ -88,6 +164,10 @@ function OptionsMenu:_init(parent, title_font, button_font) parent.next_menu = constants.MAIN_MENU end)) self.container:add(box) + + -- Register the dialog with the asset manager. + assets:register(self.name, dlg) + assets:register(self.name, self.subtitle_font) end diff --git a/src/ui/bar.lua b/src/ui/bar.lua index 310b133..db7d088 100644 --- a/src/ui/bar.lua +++ b/src/ui/bar.lua @@ -21,7 +21,7 @@ local Bar = make_class(UIElement) ------------------------------------------------------------------------------ function Bar:_init(callback, val_fn, min, max, w, h, color, sel_color, press_col, float, x, y) - UIElement._init(self, x, y, float) + UIElement._init(self, x, y, float, false) self.callback = callback self.val_fn = val_fn self.min = min diff --git a/src/ui/button.lua b/src/ui/button.lua index 9ce51ac..3e491ec 100644 --- a/src/ui/button.lua +++ b/src/ui/button.lua @@ -11,6 +11,7 @@ local UIElement = require 'src.ui.element' -- Class definitions ------------------------------------------------------------------------------ +---@class Button: UIElement local Button = make_class(UIElement) @@ -18,8 +19,12 @@ local Button = make_class(UIElement) -- Class methods ------------------------------------------------------------------------------ +---@param x number +---@param y number +---@param callback function +---@param float boolean function Button:_init(x, y, callback, float) - UIElement._init(self, x, y, float) + UIElement._init(self, x, y, float, false) self.selected = false self.pressed = false self.was_pressed = false diff --git a/src/ui/dialog.lua b/src/ui/dialog.lua new file mode 100644 index 0000000..1e97535 --- /dev/null +++ b/src/ui/dialog.lua @@ -0,0 +1,117 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local love = require 'love' +local make_class = require 'src.utils.classes' +local dialog_manager = require 'src.ui.dlgmngr' +local VBox = require 'src.ui.vbox' +local Sprite = require 'src.graphics.sprite' +local TextButton = require 'src.ui.textbtn' +local HBox = require 'src.ui.hbox' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local Dialog = make_class(VBox) + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function Dialog:_init(bckg, spacing, padding, close_btn, accept_btn, font) + VBox._init(self, nil, nil, nil, nil, spacing, false) + self.spacing = spacing ~= nil and spacing or 10 + self.padding = padding ~= nil and padding or 15 + self.background = Sprite(bckg) + self.shown = false + + local close = nil + + if close_btn ~= nil then + close = close_btn + else + close = TextButton( + 'Close', + font, + nil, + nil, + function() + self:hide() + end) + end + + local accept = nil + if accept_btn ~= nil then accept = accept_btn end + + -- Create a container for the close and accept button and add them. + local box = HBox(nil, nil, nil, nil, 10, true) + box:add(close) + + if accept ~= nil then box:add(accept) end + + self.elements[9001] = box +end + + +function Dialog:add(element) + table.insert(self.elements, element) +end + + +function Dialog:load() + self.background:load() + self:set_dimensions() + VBox.load(self) +end + + +function Dialog:set_dimensions() + self.w = self.background.sprite:getWidth() + self.h = self.background.sprite:getHeight() + + -- Center the dialog on the screen. + self.x = (320 - self.w) / 2 + self.y = (200 - self.h) / 2 + + self.background.x = self.x + self.background.y = self.y + + -- Add padding. + self.x = self.x + self.padding + self.y = self.y + self.padding / 2 +end + + +function Dialog:draw() + love.graphics.setColor(0, 0, 0) + love.graphics.rectangle('line', self.x - self.padding - 1, self.y - (self.padding / 2) - 1, self.w + 2, self.h + 2) + love.graphics.setColor() + + self.background:draw() + VBox.draw(self) +end + + +function Dialog:show() + if not self.shown then + dialog_manager:add(self) + self.shown = true + end +end + + +function Dialog:hide() + dialog_manager:remove(self) + self.shown = false +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return Dialog diff --git a/src/ui/dlgmngr.lua b/src/ui/dlgmngr.lua new file mode 100644 index 0000000..4c2f57f --- /dev/null +++ b/src/ui/dlgmngr.lua @@ -0,0 +1,104 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local make_class = require 'src.utils.classes' +local Drawable = require 'src.graphics.drawable' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local DialogManager = make_class(Drawable) + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +function DialogManager:_init() + Drawable._init(self) + self.dialogs = {} + self.count = 0 +end + + +function DialogManager:add(dialog) + self.dialogs[dialog] = true + self.count = self.count + 1 +end + + +function DialogManager:remove(dialog) + self.dialogs[dialog] = nil + self.count = math.max(self.count - 1, 0) +end + + +function DialogManager:empty() + return self.count == 0 +end + + +function DialogManager:update(dt) + for d, _ in pairs(self.dialogs) do + d:update(dt) + end +end + + +function DialogManager:draw() + for d, _ in pairs(self.dialogs) do + d:draw() + end +end + + +function DialogManager:keypressed(key, code, isrepeat) + for d, _ in pairs(self.dialogs) do + d:keypressed(key, code, isrepeat) + end +end + + +function DialogManager:textinput(text) + for d, _ in pairs(self.dialogs) do + d:textinput(text) + end +end + + +function DialogManager:keyreleased(key, code) + for d, _ in pairs(self.dialogs) do + d:keyreleased(key, code) + end +end + + +function DialogManager:mousemoved(x, y, dx, dy) + for d, _ in pairs(self.dialogs) do + d:mousemoved(x, y, dx, dy) + end +end + + +function DialogManager:mousepressed(x, y, btn) + for d, _ in pairs(self.dialogs) do + d:mousepressed(x, y, btn) + end +end + + +function DialogManager:mousereleased(x, y, btn) + for d, _ in pairs(self.dialogs) do + d:mousereleased(x, y, btn) + end +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return DialogManager() diff --git a/src/ui/element.lua b/src/ui/element.lua index 13cd2ee..c880f8f 100644 --- a/src/ui/element.lua +++ b/src/ui/element.lua @@ -6,11 +6,18 @@ local make_class = require 'src.utils.classes' local Asset = require 'src.utils.asset' local Drawable = require 'src.graphics.drawable' +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +local focused_element = nil + ------------------------------------------------------------------------------ -- Class definitions ------------------------------------------------------------------------------ +---@class UIElement: Drawable, Asset local UIElement = make_class(Drawable, Asset) @@ -18,13 +25,18 @@ local UIElement = make_class(Drawable, Asset) -- Class methods ------------------------------------------------------------------------------ -function UIElement:_init(x, y, float) +---@param x integer +---@param y integer +---@param float boolean +---@param focusable boolean +function UIElement:_init(x, y, float, focusable) Asset._init(self) self.x = x self.y = y self.w = nil self.h = nil self.float_right = float and float or false + self.focusable = focusable ~= nil and focusable or false end @@ -34,7 +46,6 @@ function UIElement:set_dimensions() end - function UIElement:load() end @@ -48,6 +59,24 @@ function UIElement:is_loaded() end +function UIElement:focus() + if self.focusable then + focused_element = self + end +end + + +function UIElement:blur() + if self.focusable and focused_element == self then + focused_element = nil + end +end + + +function UIElement:is_focused() + return self.focusable and focused_element == self +end + ------------------------------------------------------------------------------ -- Module return ------------------------------------------------------------------------------ diff --git a/src/ui/font.lua b/src/ui/font.lua index 1472405..4cc8b44 100644 --- a/src/ui/font.lua +++ b/src/ui/font.lua @@ -10,6 +10,7 @@ local Asset = require 'src.utils.asset' -- Class definitions ------------------------------------------------------------------------------ +---@class Font: Asset -- Font is a simple wrapper around Löve's own Font class to allow for -- easy loading/unloading and automatically checking if it's is valid -- before using it. @@ -20,6 +21,8 @@ local Font = make_class(Asset) -- Class methods ------------------------------------------------------------------------------ +---@param file_name string +---@param size number function Font:_init(file_name, size) self.file_name = string.format('assets/%s', file_name) self.size = (size ~= nil and size) or 20 diff --git a/src/ui/hbox.lua b/src/ui/hbox.lua index dd88b8e..019fb0c 100644 --- a/src/ui/hbox.lua +++ b/src/ui/hbox.lua @@ -10,6 +10,7 @@ local Layout = require 'src.ui.layout' -- Class definitions ------------------------------------------------------------------------------ +---@class HBox: Layout local HBox = make_class(Layout) @@ -17,8 +18,8 @@ local HBox = make_class(Layout) -- Class methods ------------------------------------------------------------------------------ -function HBox:_init(x, y, w, h, spacing) - Layout._init(self, x, y, w, h, spacing) +function HBox:_init(x, y, w, h, spacing, float) + Layout._init(self, x, y, w, h, spacing, float) end diff --git a/src/ui/label.lua b/src/ui/label.lua index f2cf60e..3f9cd28 100644 --- a/src/ui/label.lua +++ b/src/ui/label.lua @@ -12,6 +12,7 @@ local Color = require 'src.utils.color' -- Class definitions ------------------------------------------------------------------------------ +---@class Label: UIElement local Label = make_class(UIElement) @@ -19,8 +20,14 @@ local Label = make_class(UIElement) -- Class methods ------------------------------------------------------------------------------ +---@param x number +---@param y number +---@param text string +---@param font Font +---@param color Color +---@param float boolean function Label:_init(x, y, text, font, color, float) - UIElement._init(self, x, y, float) + UIElement._init(self, x, y, float, false) self.text = text self.font = font self.color = color ~= nil and color or Color(255, 255, 255) @@ -34,9 +41,7 @@ end function Label:load() - if not self.font:is_loaded() then - self.font:load() - end + self.font:load() -- 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 diff --git a/src/ui/layout.lua b/src/ui/layout.lua index 3e06dc5..7a9873b 100644 --- a/src/ui/layout.lua +++ b/src/ui/layout.lua @@ -10,6 +10,7 @@ local UIElement = require 'src.ui.element' -- Class definitions ------------------------------------------------------------------------------ +---@class Layout: UIElement local Layout = make_class(UIElement) @@ -18,7 +19,7 @@ local Layout = make_class(UIElement) ------------------------------------------------------------------------------ function Layout:_init(x, y, w, h, spacing, float) - UIElement._init(self, x, y, float) + UIElement._init(self, x, y, float, false) self.x = x ~= nil and x or 0 self.y = y ~= nil and y or 0 self.w = w @@ -68,6 +69,27 @@ function Layout:draw() end +function Layout:keypressed(key, code, isrepeat) + for _, v in pairs(self.elements) do + v:keypressed(key, code, isrepeat) + end +end + + +function Layout:textinput(text) + for _, v in pairs(self.elements) do + v:textinput(text) + end +end + + +function Layout:keyreleased(key, code) + for _, v in pairs(self.elements) do + v:keyreleased(key, code) + end +end + + function Layout:mousemoved(x, y, dx, dy) for _, v in pairs(self.elements) do v:mousemoved(x, y, dx, dy) diff --git a/src/ui/textinpt.lua b/src/ui/textinpt.lua new file mode 100644 index 0000000..664d185 --- /dev/null +++ b/src/ui/textinpt.lua @@ -0,0 +1,118 @@ +------------------------------------------------------------------------------ +-- Imports +------------------------------------------------------------------------------ + +local love = require 'love' +local make_class = require 'src.utils.classes' +local collisions = require 'src.utils.colls' +local UIElement = require 'src.ui.element' +local Color = require 'src.utils.color' + + +------------------------------------------------------------------------------ +-- Class definitions +------------------------------------------------------------------------------ + +---@class CharInput: UIElement +local TextInput = make_class(UIElement) + + +------------------------------------------------------------------------------ +-- Class methods +------------------------------------------------------------------------------ + +---@param text string +---@param font Font +---@param x number +---@param y number +---@param callback function +---@param value function +---@param float boolean +---@param base_col Color +---@param sel_color Color +---@param press_col Color +function TextInput:_init(text, font, x, y, callback, value, float, base_col, sel_color, press_col) + UIElement._init(self, x, y, float, true) + self.callback = callback + self.value = value + 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) + self.disbl_col = Color(95, 63, 75) + self.selected = false + self.pressed = false +end + + +function TextInput:load() + self.font:load() + + -- 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 TextInput:unload() + self.font:unload() +end + + +function TextInput:is_loaded() + return self.font:is_loaded() and self.w ~= nil and self.h ~= nil +end + + +function TextInput:set_dimensions() + self.w = self.font:get_width(self.text .. 'm') + self.h = self.font:get_height(self.text) +end + + +function TextInput:draw() + local color = self.callback == nil and self.disbl_col or (((self.pressed or self:is_focused()) 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() + + if self:is_focused() then + love.graphics.print(self.text .. ': ?', self.x, self.y) + else + love.graphics.print(self.text .. ': ' .. self.value(), self.x, self.y) + end + + self.font:unset() + love.graphics.setColor() +end + + +function TextInput: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) +end + + +function TextInput:mousepressed(_, _, btn) + -- If the pointer was inside the button when it was pressed then mark the button as pressed. + if btn == 0 and self.selected then + self:focus() + end +end + + +function TextInput:keypressed(key) + if self:is_focused() then + if #key == 1 then + if self.callback ~= nil then self.callback(key) end + self:blur() + end + end +end + + +------------------------------------------------------------------------------ +-- Module return +------------------------------------------------------------------------------ + +return TextInput diff --git a/src/ui/vbox.lua b/src/ui/vbox.lua index fac1def..ea2e9f7 100644 --- a/src/ui/vbox.lua +++ b/src/ui/vbox.lua @@ -10,6 +10,7 @@ local Layout = require 'src.ui.layout' -- Class definitions ------------------------------------------------------------------------------ +---@class VBox: Layout local VBox = make_class(Layout) @@ -17,8 +18,8 @@ local VBox = make_class(Layout) -- Class methods ------------------------------------------------------------------------------ -function VBox:_init(x, y, w, h, spacing) - Layout._init(self, x, y, w, h, spacing) +function VBox:_init(x, y, w, h, spacing, float) + Layout._init(self, x, y, w, h, spacing, float) end diff --git a/src/utils/asset.lua b/src/utils/asset.lua index 5792bf6..22ffe95 100644 --- a/src/utils/asset.lua +++ b/src/utils/asset.lua @@ -9,6 +9,7 @@ local make_class = require 'src.utils.classes' -- Class definitions ------------------------------------------------------------------------------ +---@class Asset local Asset = make_class() diff --git a/src/utils/classes.lua b/src/utils/classes.lua index f302812..f2813cc 100644 --- a/src/utils/classes.lua +++ b/src/utils/classes.lua @@ -2,7 +2,9 @@ -- Methods ------------------------------------------------------------------------------ --- Code from http://lua-users.org/wiki/ObjectOrientationTutorial +--- Code from http://lua-users.org/wiki/ObjectOrientationTutorial +---@param ... table Optional base classes +---@return table cls A class definition local function make_class(...) -- "cls" is the new class local cls, bases = {}, {...} diff --git a/src/utils/color.lua b/src/utils/color.lua index 9843f30..1027b26 100644 --- a/src/utils/color.lua +++ b/src/utils/color.lua @@ -9,6 +9,7 @@ local make_class = require 'src.utils.classes' -- Class definitions ------------------------------------------------------------------------------ +---@class Color local Color = make_class() diff --git a/src/utils/settings.lua b/src/utils/settings.lua index aa6ac6d..d586644 100644 --- a/src/utils/settings.lua +++ b/src/utils/settings.lua @@ -4,6 +4,7 @@ local love = require 'love' local magiclines = require 'src.utils.mgclines' +local make_class = require 'src.utils.classes' ------------------------------------------------------------------------------ @@ -17,25 +18,29 @@ local SETTINGS_PATH = 'settings.ini' -- Module definitions ------------------------------------------------------------------------------ -local settings = { - -- Default setting values. - permadeath = true, - forward = 'w', - backward = 's', - stepleft = 'a', - stepright = 'd', - turnleft = 'q', - turnright = 'e', - musicvol = 75, - soundvol = 100, -} +---@class Settings +local Settings = make_class() ------------------------------------------------------------------------------ -- Private module methods ------------------------------------------------------------------------------ -function settings:_parse_settings_str(s) +function Settings:_init() + -- Default setting values. + self.permadeath = true + self.forward = 'w' + self.backward = 's' + self.stepleft = 'a' + self.stepright = 'd' + self.turnleft = 'q' + self.turnright = 'e' + self.musicvol = 75 + self.soundvol = 100 +end + + +function Settings:_parse_settings_str(s) -- For each line in the data string. for line in magiclines(s) do -- Check if the line matches a key=value pair @@ -67,7 +72,7 @@ function settings:_parse_settings_str(s) end -function settings:_get_settings_str() +function Settings:_get_settings_str() -- Start with the settings category. local s = '[settings]\n' @@ -84,7 +89,7 @@ function settings:_get_settings_str() end -function settings:_open_or_create() +function Settings:_open_or_create() if not love.filesystem.exists(SETTINGS_PATH) then -- If the settings file doesn't exist then create it with the default values. local setts = self:_get_settings_str() @@ -106,14 +111,14 @@ end -- Public module methods ------------------------------------------------------------------------------ -function settings:load_settings() +function Settings:load_settings() -- Obtain the settings data and parse it. local s = self:_open_or_create() self:_parse_settings_str(s) end -function settings:save_settings() +function Settings:save_settings() if not love.filesystem.exists(SETTINGS_PATH) or love.filesystem.isFile(SETTINGS_PATH) then -- If the settings file doesn't exist or it exists and is a file then save the current data. love.filesystem.write(SETTINGS_PATH, self:_get_settings_str()) @@ -128,4 +133,4 @@ end -- Module return ------------------------------------------------------------------------------ -return settings +return Settings()