From 12ef4728a862c358ce219372b57d68a879073975 Mon Sep 17 00:00:00 2001 From: Kazhnuz Date: Fri, 27 Aug 2021 00:44:01 +0200 Subject: [PATCH] feat: add birb's menu to the gui system --- .../birb/modules/gui/elements/parent.lua | 2 +- .../birb/modules/gui/menus/flowbox.lua | 144 +++++++++ .../birb/modules/gui/menus/grid.lua | 276 ++++++++++++++++++ .../birb/modules/gui/menus/hlistbox.lua | 101 +++++++ .../birb/modules/gui/menus/listbox.lua | 100 +++++++ .../birb/modules/gui/menus/model/init.lua | 189 ++++++++++++ .../birb/modules/gui/menus/model/page.lua | 185 ++++++++++++ .../birb/modules/gui/menus/parent.lua | 243 +++++++++++++++ .../birb/modules/gui/menus/views/view1D.lua | 49 ++++ .../birb/modules/gui/menus/views/view2D.lua | 61 ++++ .../birb/modules/gui/menus/widgets/base.lua | 156 ++++++++++ .../birb/modules/gui/menus/widgets/init.lua | 30 ++ .../birb/modules/gui/menus/widgets/text.lua | 95 ++++++ .../birb/modules/gui/mixins/elements.lua | 6 + .../birb/modules/gui/textmenu/init.lua | 78 +++++ .../modules/gui/textmenu/widgets/back.lua | 37 +++ .../modules/gui/textmenu/widgets/basic.lua | 47 +++ .../modules/gui/textmenu/widgets/init.lua | 31 ++ .../modules/gui/textmenu/widgets/submenu.lua | 39 +++ .../birb/modules/gui/utils.lua | 57 ++++ 20 files changed, 1925 insertions(+), 1 deletion(-) create mode 100644 sonic-radiance.love/birb/modules/gui/menus/flowbox.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/grid.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/hlistbox.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/listbox.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/model/init.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/model/page.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/parent.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/views/view1D.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/views/view2D.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/widgets/base.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/widgets/init.lua create mode 100644 sonic-radiance.love/birb/modules/gui/menus/widgets/text.lua create mode 100644 sonic-radiance.love/birb/modules/gui/textmenu/init.lua create mode 100644 sonic-radiance.love/birb/modules/gui/textmenu/widgets/back.lua create mode 100644 sonic-radiance.love/birb/modules/gui/textmenu/widgets/basic.lua create mode 100644 sonic-radiance.love/birb/modules/gui/textmenu/widgets/init.lua create mode 100644 sonic-radiance.love/birb/modules/gui/textmenu/widgets/submenu.lua create mode 100644 sonic-radiance.love/birb/modules/gui/utils.lua diff --git a/sonic-radiance.love/birb/modules/gui/elements/parent.lua b/sonic-radiance.love/birb/modules/gui/elements/parent.lua index 5cc2bc7..5103a04 100644 --- a/sonic-radiance.love/birb/modules/gui/elements/parent.lua +++ b/sonic-radiance.love/birb/modules/gui/elements/parent.lua @@ -120,7 +120,7 @@ end -- KEYBOARD FUNCTIONS -- Handle key press -function GuiElement:keyreleased(key) +function GuiElement:keypressed(key) -- Cette fonction ne contient rien par défaut end diff --git a/sonic-radiance.love/birb/modules/gui/menus/flowbox.lua b/sonic-radiance.love/birb/modules/gui/menus/flowbox.lua new file mode 100644 index 0000000..f0a40fb --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/flowbox.lua @@ -0,0 +1,144 @@ +-- flowbox :: flexible box menu, that handle in grid the widgets + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local cwd = (...):gsub('%.flowbox$', '') .. "." + +local Menu = require(cwd .. "parent") +local FlowBox = Menu:extend() + +local View2D = require(cwd .. "views.view2D") + +-- INIT FUNCTIONS +-- Initialize and configure the flowbox + +function FlowBox:new(name, x, y, w, h, slots_hor, slots_vert) + self.view = View2D(slots_hor, slots_vert) + FlowBox.super.new(self, name, x, y, w, h) + self:setRealSize() +end + +function FlowBox:setRealSize() + -- On fait en sorte que la hauteur et la largeur + -- soit un multiple du nombre de slot et de leur dimensions + self:updateWidgetSize() + self.w = self.view.colNumber * self.widgetSize.w + self.h = self.view.lineNumber * self.widgetSize.h +end + +function FlowBox:resetView() + self.view:reset() +end + +-- UPDATE FUNCTIONS +-- Update the menu and its view + +function FlowBox:updateWidgetSize() + self.widgetSize.h = math.floor( self.h / self.view.lineNumber ) + self.widgetSize.w = math.floor( self.w / self.view.colNumber ) +end + +function FlowBox:update(dt) + self.view:updateFirstSlot(self.widget:getSelected()) +end + +-- CURSOR FUNCTIONS +-- Handle the cursor in a 2D menu + +function FlowBox:moveCursor2D(new_col, new_line) + local lastcol, lastline = self.view:getCoord(self.widget:lenght()) + + new_line = utils.math.wrapAndLimit(new_line, 0, lastline) + + if (new_line == lastline) then + new_col = utils.math.wrapAndLimit(new_col, 0, lastcol) + else + new_col = utils.math.wrapAndLimit(new_col, 0, (self.view.colNumber - 1)) + end + + self.widget:moveCursor((new_line * self.view.colNumber) + new_col + 1) +end + +-- KEYS FUNCTIONS +-- Handle the keyboard/controller inputs + +function FlowBox:moveByKeys(key) + local col, line = self.view:getCoord(self.widget:getSelected()) + if key == 'left' then + self:moveCursor2D(col - 1, line) + end + + if key == 'right' then + self:moveCursor2D(col + 1, line) + end + + if key == 'up' then + self:moveCursor2D(col, line - 1) + end + + if key == 'down' then + self:moveCursor2D(col, line + 1) + end +end + +-- POSITION FUNCTIONS +-- Get a widget by a position. + +function FlowBox:getWidgetAtPoint(x, y) + local col = math.floor(x / self.widgetSize.w) + local line = math.floor(y / self.widgetSize.h) + return self.view:getFromCoord(col, line) +end + +-- DRAW FUNCTIONS +-- Draw the menu and its content + +function FlowBox:draw() + self.view:updateFirstSlot(self.widget:getSelected()) + local widgety = self.y + local widgetx = self.x + + local listWidget = self.widget:getList(self.view.firstSlot, self.view.slotNumber) + + for _, widget in ipairs(listWidget) do + widget:drawWidget(widgetx, widgety, self.w, self.widgetSize.h) + + -- On calcule la position du prochain widget + widgetx = widgetx + self.widgetSize.w + if widgetx >= (self.x + self.w) then + widgetx = self.x + widgety = widgety + self.widgetSize.h + end + end +end + +function FlowBox:getGraphicalCursorPosition() + self.view:updateFirstSlot(self.widget:getSelected()) + local _, beginline = self.view:getCoord(self.view.firstSlot) + local w, h = self:getWidgetSize() + local col, line = self.view:getCoord(self.widget:getSelected()) + local x = (col) * h + local y = (line - beginline) * h + return self.x + x, self.y + y, w, h +end + +return FlowBox diff --git a/sonic-radiance.love/birb/modules/gui/menus/grid.lua b/sonic-radiance.love/birb/modules/gui/menus/grid.lua new file mode 100644 index 0000000..8cd62e3 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/grid.lua @@ -0,0 +1,276 @@ +-- grid :: a menu with arbitrary widget placement and size on a grid. + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local cwd = (...):gsub('%.grid$', '') .. "." + +local Menu = require(cwd .. "parent") +local GridBox = Menu:extend() + +local View2D = require "birb.modules.gui.menus.views.view2D" + +-- INIT FUNCTIONS +-- Initialize and configure the menu + +function GridBox:new(name, x, y, w, h, colNumber, lineNumber) + self.view = View2D(colNumber, lineNumber) + GridBox.super.new(self, name, x, y, w, h) + self.h = lineNumber * self.widgetSize.h -- On fait en sorte que la hauteur + self.w = colNumber * self.widgetSize.w -- et la largeur + -- soit un multiple du nombre de slot et de leur dimensions + self.cursor = {} + self.cursor.x = 0 + self.cursor.y = 0 + + -- La gridbox possède la particularité de pouvoir fusioner des slots, on fait + -- donc une liste de slots disponibles, qui serviront par la suite. + self.slots = {} +end + +function GridBox:addSlot(widgetID, x, y, w, h) + local slot = {} + slot.x = x + slot.y = y + slot.w = w + slot.h = h + + table.insert(self.slots, slot) +end + +function GridBox:updateWidgetSize() + self.widgetSize.h = math.floor( self.h / self.view.lineNumber ) + self.widgetSize.w = math.floor( self.w / self.view.colNumber ) +end + +-- INFO FUNCTIONS +-- Get the info of the widgets + +function GridBox:getWidgetSize(id) + local slot = self:getWidgetSlot(id) + if (slot == 0) then + return 1, 1 + else + return self.widgetSize.w * self.slots[slot].w, self.widgetSize.h * self.slots[slot].h + end +end + +function GridBox:getSlotHitbox(slot) + local x, y, w, h + x = self.slots[slot].x * self.widgetSize.w + y = self.slots[slot].y * self.widgetSize.h + w = self.slots[slot].w * self.widgetSize.w + h = self.slots[slot].h * self.widgetSize.h + + return x, y, w, h +end + +function GridBox:getSlotCenter(slot) + local x, y, w, h = self:getSlotHitbox(slot) + + return x + (w/2), y + (h/2) +end + +function GridBox:getWidgetID(slot) + local widgetID + if self.slots[slot] ~= nil then + widgetID = self.slots[slot].widgetID + else + widgetID = 0 + end + return widgetID +end + +function GridBox:haveWidget(slot) + local id = self:getWidgetID(slot) + return self.widget.list[id] ~= nil +end + +function GridBox:getWidgetSlot(widgetID) + local slot = 0 + for i,v in ipairs(self.slots) do + if (self.slots[i].widgetID == widgetID) then + slot = i + end + end + + return slot +end + +function GridBox:getWidgetAtPoint(x, y) + local x = x or 0 + local y = y or 0 + local widgetID = nil + + for i,v in ipairs(self.slots) do + local xx, yy, ww, hh = self:getSlotHitbox(i) + if (x >= xx) and (y >= yy) and (x < xx + ww) and (y < yy + hh) then + widgetID = v.widgetID + end + end + + return widgetID +end + +-- UPDATE FUNCTIONS +-- Update the Grid and its view + +function GridBox:update(dt) + self.view.firstSlot = 1 +end + +function GridBox:resetView() + self.view:reset() +end + + +-- KEYS FUNCTIONS +-- Handle the keyboard/manette functions + +function GridBox:keyreleased(key, code) + local slotID = self:getWidgetSlot(self.widget:getSelected()) + local col, line = self.cursor.x, self.cursor.y + if key == 'left' then + self:moveCol(-1) + end + + if key == 'right' then + self:moveCol(1) + end + + if key == 'up' then + self:moveLine(-1) + end + + if key == 'down' then + self:moveLine(1) + end + + if key == "A" and self.widget:getSelected() <= self.widget:lenght() then + self.widget.list[self.widget:getSelected()]:action("key") + end +end + +function GridBox:moveCol(direction) + local orig_x, orig_y = self:getSlotCenter(self.widget:getSelected()) + local distance = self.w -- on met directement à la distance max possible le système + local nearestWidget = 0 + for i,v in ipairs(self.slots) do + local xx, yy = self:getSlotCenter(i) + -- On commence par vérifier si le slot est bien positionné par rapport au + -- widget de base + if utils.math.sign(xx - orig_x) == direction then + if utils.math.pointDistance(orig_x, orig_y, xx, yy) < distance then + distance = utils.math.pointDistance(orig_x, orig_y, xx, yy) + nearestWidget = v.widgetID + end + end + end + + if (nearestWidget ~= 0) then + self.widget:setCursor(nearestWidget) + end +end + +function GridBox:moveLine(direction) + local orig_x, orig_y = self:getSlotCenter(self.widget:getSelected()) + local distance = self.h -- on met directement à la distance max possible le système + local nearestWidget = 0 + for i,v in ipairs(self.slots) do + local xx, yy = self:getSlotCenter(i) + -- On commence par vérifier si le slot est bien positionné par rapport au + -- widget de base + if utils.math.sign(yy - orig_y) == direction then + if utils.math.pointDistance(orig_x, orig_y, xx, yy) < distance then + distance = utils.math.pointDistance(orig_x, orig_y, xx, yy) + nearestWidget = v.widgetID + end + end + end + + if (nearestWidget ~= 0) then + self.widget:setCursor(nearestWidget) + end +end + +-- MOUSE FUNCTIONS +-- Handle the mouse and activate the widgets with it + +function GridBox:mousemoved(x, y) + local widgetID = self:getWidgetAtPoint(x, y) + + -- if (widgetID ~= nil) then + -- self.widget:getSelected() = widgetID + -- self:getFocus() + -- end + + -- if self.widget:getSelected() < 1 then + -- self.widget:getSelected() = 1 + -- end + -- if self.widget:getSelected() > self.widget:lenght() then + -- self.widget:getSelected() = self.widget:lenght() + -- end + +end + +function GridBox:mousepressed(x, y, button, isTouch) + local widgetID = self:getWidgetAtPoint(x, y) + + -- if (widgetID ~= nil) then + -- self.widget:getSelected() = widgetID + -- self:getFocus() + + -- if self.widget:lenght() > 0 and self.widget:getSelected() > 1 and self.widget:getSelected() <= self.widget:lenght() then + -- self.widget.list[self.widget:getSelected()]:action("pointer") + -- end + -- end +end + +-- DRAW FUNCTIONS +-- Draw the menu and its content + +function GridBox:draw() + + for i,v in ipairs(self.slots) do + if self:haveWidget(i) then + local widgetx = self.x + (v.x * self.widgetSize.w) + local widgety = self.y + (v.y * self.widgetSize.h) + if self.widget:getSelected() == v.widgetID and self:haveFocus() == true then + self.widget.list[v.widgetID]:drawSelected(widgetx, widgety) + else + self.widget.list[v.widgetID]:draw(widgetx, widgety) + end + end + end +end + +function GridBox:drawCursor() + self:updateView() + if (self.widget:getSelected() >= 1 and self.widget:getSelected() <= self.widget:lenght()) then + local slot = self:getWidgetSlot(self.widget:getSelected()) + local w, h = self:getWidgetSize(slot) + local x = self.slots[slot].x * self.widgetSize.w + local y = self.slots[slot].y * self.widgetSize.h + self:drawGraphicalCursor(self.x + x, self.y + y, w, h) + end +end + +return GridBox diff --git a/sonic-radiance.love/birb/modules/gui/menus/hlistbox.lua b/sonic-radiance.love/birb/modules/gui/menus/hlistbox.lua new file mode 100644 index 0000000..faec6bc --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/hlistbox.lua @@ -0,0 +1,101 @@ +-- hlistbox : add an horizontal list of widgets. + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local cwd = (...):gsub('%.hlistbox$', '') .. "." + +local Menu = require(cwd .. "parent") +local HListBox = Menu:extend() + +local View1D = require(cwd .. "views.view1D") + +-- INIT FUNCTIONS +-- Initialize and configure functions. + +function HListBox:new(name, x, y, w, h, slotNumber) + self.view = View1D(slotNumber) + HListBox.super.new(self, name, x, y, w, h) + self.w = slotNumber * self.widgetSize.w -- On fait en sorte que la hauteur + -- soit un multiple du nombre de slot et de leur hauteur +end + +-- UPDATE FUNCTIONS +-- Update the menu every step. + +function HListBox:updateWidgetSize() + self.widgetSize.h = self.h + self.widgetSize.w = math.floor( self.w / self.view.slotNumber ) +end + +function HListBox:update(dt) + self.view:updateFirstSlot(self.widget:getSelected()) +end + +function HListBox:resetView() + self.view:reset() +end + +-- KEYBOARD FUNCTIONS +-- Handle key check. + +function HListBox:moveByKeys(key, code) + if key == 'left' then + self.widget:moveCursor(-1) + end + + if key == 'right' then + self.widget:moveCursor(1) + end +end + +-- POSITION FUNCTIONS +-- Get a widget by a position. + +function HListBox:getWidgetAtPoint(x, y) + return (self.view.firstSlot + math.floor(x / self.widgetSize.w)) +end + +-- DRAW FUNCTIONS +-- Draw the menu and its content + +function HListBox:draw() + self.view:updateFirstSlot(self.widget:getSelected()) + + local widgetx = self.x + + local listWidget = self.widget:getList(self.view.firstSlot, self.view.slotNumber) + + for _, widget in ipairs(listWidget) do + widget:drawWidget(widgetx, self.y, self.widgetSize.w, self.h) + widgetx = widgetx + self.widgetSize.w + end +end + +function HListBox:getGraphicalCursorPosition() + self.view:updateFirstSlot(self.widget:getSelected()) + local w, h = self:getWidgetSize() + local x = (self.widget:getSelected() - self.view.firstSlot) * w + + return self.x + x,self.y, w, h +end + +return HListBox diff --git a/sonic-radiance.love/birb/modules/gui/menus/listbox.lua b/sonic-radiance.love/birb/modules/gui/menus/listbox.lua new file mode 100644 index 0000000..d99d614 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/listbox.lua @@ -0,0 +1,100 @@ +-- listbox : add a vertical list of widgets. + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local cwd = (...):gsub('%.listbox$', '') .. "." +local Menu = require(cwd .. "parent") + +local ListBox = Menu:extend() + +local View1D = require(cwd .. "views.view1D") + +-- INIT FUNCTIONS +-- Initialize and configure functions. + +function ListBox:new(name, x, y, w, h, slotNumber) + self.view = View1D(slotNumber) + ListBox.super.new(self, name, x, y, w, h) + self.h = slotNumber * self.widgetSize.h -- On fait en sorte que la hauteur + -- soit un multiple du nombre de slot et de leur hauteur +end + +-- UPDATE FUNCTIONS +-- Update the menu every step. + +function ListBox:updateWidgetSize() + self.widgetSize.h = math.floor( self.h / self.view.slotNumber ) + self.widgetSize.w = self.w +end + +function ListBox:update(dt) + self.view:updateFirstSlot(self.widget:getSelected()) +end + +function ListBox:resetView() + self.view:reset() +end + +-- KEYBOARD FUNCTIONS +-- Handle input from keyboard/controllers. + +function ListBox:moveByKeys(key) + if key == 'up' then + self.widget:moveCursor(-1) + end + + if key == 'down' then + self.widget:moveCursor(1) + end +end + +-- POSITION FUNCTIONS +-- Get a widget by a position. + +function ListBox:getWidgetAtPoint(x, y) + return (self.view.firstSlot + math.floor(y / self.widgetSize.h)) +end + +-- DRAW FUNCTIONS +-- draw the menu and the rest of content. + +function ListBox:draw() + self.view:updateFirstSlot(self.widget:getSelected()) + local widgety = self.y + + local listWidget = self.widget:getList(self.view.firstSlot, self.view.slotNumber) + + for _, widget in ipairs(listWidget) do + widget:drawWidget(self.x, widgety, self.w, self.widgetSize.h) + widgety = widgety + self.widgetSize.h + end +end + +function ListBox:getGraphicalCursorPosition() + self.view:updateFirstSlot(self.widget:getSelected()) + local w, h = self:getWidgetSize() + local y = (self.widget:getSelected() - self.view.firstSlot) * h + + return self.x,self.y + y, w, h +end + +return ListBox diff --git a/sonic-radiance.love/birb/modules/gui/menus/model/init.lua b/sonic-radiance.love/birb/modules/gui/menus/model/init.lua new file mode 100644 index 0000000..2aba865 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/model/init.lua @@ -0,0 +1,189 @@ +local MenuModel = Object:extend() +local Page = require "birb.modules.gui.menus.model.page" + +local function updateWidgetByOrder(a, b) + if a.order ~= b.order then + return a.order < b.order + else + return a.creationID < b.creationID + end +end + +-- INIT FUNCTIONS +-- Initialize and basic functions. + +function MenuModel:new() + self:clear() + + self.list = {} + self.selected = 0 + self.selectedPrevious = 0 + self.cancel = 0 + self.limit = -1 + -- self:updateWidgetSize() +end + +function MenuModel:clear() + self.pages = {} + self:addPage("main") +end + +-- PAGE FUNCTIONS +-- Handle pages + +function MenuModel:addPage(pageName) + local page = Page() + self.pages[pageName] = page + self.currentPage = pageName + return page +end + +function MenuModel:addSubmenu(pageName, parent) + local page = self:addPage(pageName) + page:setParent(parent) +end + +function MenuModel:removePage(pageName) + self.pages[pageName] = nil +end + +function MenuModel:pageExists(pageName) + return (self.pages[pageName] ~= nil) +end + +function MenuModel:getPage(pageName) + return self.pages[pageName] +end + +function MenuModel:switch(pageName) + if (self:pageExists(pageName)) then + self.currentPage = pageName + end +end + +function MenuModel:back() + local page = self:getCurrentPage() + self:switch(page:getParent()) +end + +function MenuModel:getCurrentPage() + return self:getPage(self.currentPage) +end + +-- UPDATE/DRAW FUNCTIONS +-- All the update functions + +function MenuModel:update(dt) + local page = self:getCurrentPage() + page:update(dt) +end + +function MenuModel:updateWidgetsOrder() + local page = self:getCurrentPage() + page:updateWidgetByOrder() +end + +function MenuModel:updateWidgetsID() + local page = self:getCurrentPage() + page:updateWidgetsID() +end + +-- ACTION FUNCTIONS +-- All the actions callback used by the widgets + +function MenuModel:cancelAction() + local page = self:getCurrentPage() + page:cancelAction() +end + +function MenuModel:action(id, type) + local page = self:getCurrentPage() + page:action(id, type) +end + +function MenuModel:selectedAction() + local page = self:getCurrentPage() + page:selectedAction() +end + +-- WIDGET FUNCTIONS +-- All the functions to handle widgets + +function MenuModel:addWidget(newWidget) + local page = self:getCurrentPage() + page:addWidget(newWidget) +end + +function MenuModel:getList(first, lenght) + local page = self:getCurrentPage() + return page:getList(first, lenght) +end + +function MenuModel:removeDestroyedWidgets() + local page = self:getCurrentPage() + page:removeDestroyedWidgets() +end + +function MenuModel:lenght() + local page = self:getCurrentPage() + return page:lenght() +end + +function MenuModel:widgetExist(id) + local page = self:getCurrentPage() + return page:widgetExist(id) +end + +function MenuModel:setLimit(limit) + local page = self:getCurrentPage() + page:setLimit(limit) +end + +-- CANCEL FUNCTIONS +-- Add a widget as a "cancel" function + +function MenuModel:setCancelWidget() + local page = self:getCurrentPage() + page:setCancelWidget() +end + +function MenuModel:getCancelWidget() + local page = self:getCurrentPage() + return page:getCancelWidget() +end + +-- CURSOR FUNCTIONS +-- Set or move the cursor of the menu + +function MenuModel:getSelected() + local page = self:getCurrentPage() + return page:getSelected() +end + +function MenuModel:haveCursor() + local page = self:getCurrentPage() + return page:haveCursor() +end + +function MenuModel:trySelectWidget(cursorid) + local page = self:getCurrentPage() + return page:trySelectWidget(cursorid) +end + +function MenuModel:setCursor(cursorid) + local page = self:getCurrentPage() + page:setCursor(cursorid) +end + +function MenuModel:moveCursorAbsolute(newSelected) + local page = self:getCurrentPage() + page:moveCursorAbsolute(newSelected) +end + +function MenuModel:moveCursor(relative) + local page = self:getCurrentPage() + page:moveCursor(relative) +end + + +return MenuModel diff --git a/sonic-radiance.love/birb/modules/gui/menus/model/page.lua b/sonic-radiance.love/birb/modules/gui/menus/model/page.lua new file mode 100644 index 0000000..e6a0496 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/model/page.lua @@ -0,0 +1,185 @@ +local Page = Object:extend() + +local function updateWidgetByOrder(a, b) + if a.order ~= b.order then + return a.order < b.order + else + return a.creationID < b.creationID + end +end + +-- INIT FUNCTIONS +-- Initialize and basic functions. + +function Page:new() + self.widgets = {} + self.selected = 0 + self.selectedPrevious = 0 + self.cancel = 0 + self.limit = -1 + self.parent = nil + -- self:updateWidgetSize() +end + +function Page:clear() + self.widgets = {} + self.cancel = 0 +end + +function Page:setLimit(limit) + self.limit = limit +end + +-- PAGE FUNCTIONS +-- functions to handle other pages + +function Page:setParent(parent) + self.parent = parent +end + +function Page:getParent() + return self.parent +end + +-- UPDATE/DRAW FUNCTIONS +-- All the update functions + +function Page:update(dt) + self:removeDestroyedWidgets() + for id, widget in ipairs(self.widgets) do + widget.id = id + widget:update(dt) + end +end + +function Page:updateWidgetsOrder() + table.sort(self.widgets, updateWidgetByOrder) +end + +function Page:updateWidgetsID() + for id, widget in ipairs(self.widgets) do + widget.id = id + end +end + +-- ACTION FUNCTIONS +-- All the actions callback used by the widgets + +function Page:cancelAction() + if (self.cancel ~= 0) then + self:action(self.cancel, "key") + end +end + +function Page:action(id, type) + if (self:widgetExist(id)) then + self.widgets[id]:action(type) + end +end + +function Page:selectedAction() + if (self.selected ~= 0) then + self:action(self.selected, "key") + end +end + +-- WIDGET FUNCTIONS +-- All the functions to handle widgets + +function Page:addWidget(newWidget) + if (self.limit ~= -1 and #self.widgets >= self.limit) then + return + end + if #self.widgets == 0 then + self.selected = 1 + end + table.insert(self.widgets, newWidget) + self:updateWidgetsID() + self:updateWidgetsOrder() +end + +function Page:getList(first, lenght) + local listWidget = {} + local first = first or 1 + local lenght = lenght or #self.widgets + lenght = math.min(lenght, (#self.widgets + 1 - first)) + local last = (first + lenght - 1) + + for i = first, (last) do + table.insert(listWidget, self.widgets[i]) + end + + return listWidget +end + +function Page:removeDestroyedWidgets() -- On retire les widgets marquées comme supprimées + for i, v in ipairs(self.widgets) do + if (v.destroyed == true) then + table.remove(self.widgets, i) + end + end +end + +function Page:lenght() + return #self.widgets +end + +function Page:widgetExist(id) + local id = id or 0 + return (id >= 1 and id <= #self.widgets) +end + +-- CANCEL FUNCTIONS +-- Add a widget as a "cancel" function + +function Page:setCancelWidget(id) + self.cancel = #self.widgets +end + +function Page:getCancelWidget(id) + return self.cancel +end + +-- CURSOR FUNCTIONS +-- Set or move the cursor of the menu + +function Page:getSelected() + return self.selected +end + +function Page:haveCursor() + return self:widgetExist(self.selected) +end + +function Page:trySelectWidget(cursorid) + if (self:widgetExist(cursorid)) then + self:setCursor(cursorid) + return true + else + return false + end +end + +function Page:setCursor(cursorid) + self.selected = cursorid --math.max(1, math.min(cursorid, #self.widgets)) +end + +function Page:moveCursorAbsolute(newSelected) + -- self:playNavigationSound() + if newSelected < 1 then + self.selected = #self.widgets + newSelected + else + if newSelected > #self.widgets then + self.selected = newSelected - #self.widgets + else + self.selected = newSelected + end + end +end + +function Page:moveCursor(relative) + self:moveCursorAbsolute(self.selected + relative) +end + + +return Page diff --git a/sonic-radiance.love/birb/modules/gui/menus/parent.lua b/sonic-radiance.love/birb/modules/gui/menus/parent.lua new file mode 100644 index 0000000..80f0893 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/parent.lua @@ -0,0 +1,243 @@ +-- parent.lua : The parent of the functions. + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local GuiElement = require "birb.modules.gui.elements.parent" + +local Menu = GuiElement:extend() +local MenuModel = require "birb.modules.gui.menus.model" + +local menuUtils = require "birb.modules.gui.utils" + +-- INIT FUNCTIONS +-- Initialize and configure functions. + +function Menu:new(name, x, y, w, h) + Menu.super.new(self, name, x, y, w, h) + self.menusystem = self:getGui() + + self.widget = MenuModel() + self.widgetSize = {} + self:updateWidgetSize() + + self:resetSound() +end + +-- INTERACTION FUNCTIONS +-- Keyboard and mouse + +function Menu:keypressed(key) + self:moveByKeys(key) + self:actionAndCancel(key) +end + +function Menu:mousemoved(x, y) + local widgetID = self:getWidgetAtPoint(x, y) + + if (widgetID ~= nil) then + if self.widget:trySelectWidget(widgetID) then + self:getFocus() + end + end +end + +function Menu:mousepressed(x, y, button, istouch) + local widgetID = self:getWidgetAtPoint(x, y) + + if (widgetID ~= nil) then + if self.widget:trySelectWidget(widgetID) then + self:getFocus() + self.widget:action(widgetID, "pointer") + end + end +end + +function Menu:moveByKeys(key) + -- Cette fonction ne contient rien par défaut +end + +function Menu:actionAndCancel(key) + if key == "A" then + self.widget:selectedAction() + end + + if key == "B" then + self:cancelAction() + end +end + +-- PAGE FUNCTIONS +-- Wrapper around pages functions from the model + +function Menu:addPage(pageName) + self.widget:addPage(pageName) +end + +function Menu:addSubmenu(pageName, parent) + self.widget:addSubmenu(pageName, parent) +end + +function Menu:removePage(pageName) + self.widget:removePage(pageName) +end + +function Menu:pageExists(pageName) + return self.widget:pageExists(pageName) +end + +function Menu:getPage(pageName) + self.widget:getPage(pageName) +end + +function Menu:switch(pageName) + self.widget:switch(pageName) + self:resetView() +end + +function Menu:back() + self.widget:back() + self:resetView() +end + +-- ACTION FUNCTIONS +-- Send actions to the widgets + +function Menu:cancelAction() + self.widget:cancelAction() +end + +function Menu:clear() + self.widget:clear() +end + +function Menu:resize(x,y,w,h) + self:set(x,y,w,h) + + self:updateWidgetSize() +end + +-- UPDATE FUNCTIONS + +function Menu:updateElement(dt) + self:update(dt) + self.widget:update(dt) +end + +-- DRAW FUNCTIONS +-- Draw the menu and its content + +function Menu:drawElement() + self:draw() + if (self:haveFocus()) then + local x, y, w, h = self:getGraphicalCursorPosition() + self:drawGraphicalCursor(x, y, w, h) + end +end + +function Menu:drawGraphicalCursor(x, y, w, h) + menuUtils.drawCursor(x, y, w, h) +end + +function Menu:drawCanvas() + +end + +-- WIDGET FUNCTIONS +-- Handle widgets of the functions + +function Menu:addWidget(newwidget) + self.widget:addWidget(newwidget) +end + +function Menu:setCancelWidget(id) + self.widget:setCancelWidget(id) +end + +function Menu:updateWidgetSize() + self.widgetSize.h = 0 + self.widgetSize.w = 0 +end + +function Menu:getWidgetSize(id) + return self.widgetSize.w, self.widgetSize.h +end + +function Menu:getWidgetNumber() + return self.widget:lenght() +end + +function Menu:getWidgetAtPoint(x, y) + +end + +-- CURSOR FUNCTIONS +-- Set or move the cursor of the menu + +function Menu:setCursor(cursorid) + self.widget:setCursor(cursorid) +end + +function Menu:moveCursor(new_selected) + self:playNavigationSound() + self.widget:moveCursorAbsolute(new_selected) +end + +-- SOUND FUNCTION +-- Handle SFX + +function Menu:resetSound() + self.sound = {} + self.sound.active = false + self.sound.asset = nil +end + +function Menu:setSoundFromSceneAssets(name) + self:setSound(self.menusystem.scene.assets:getWithType(name, "sfx")) +end + +function Menu:setSound(soundasset) + self.sound.active = true + self.sound.asset = soundasset +end + +function Menu:playNavigationSound() + if self.sound.active == true then + self.sound.asset:play() + end +end + +-- VIEW FUNCTIONS +-- Handle the view of the menu + +function Menu:resetView() + -- ne sert à rien ici, c'est juste pour éviter des crash +end + +function Menu:updateView() + -- ne sert à rien ici, c'est juste pour éviter des crash +end + +function Menu:moveView() + -- ne sert à rien ici, c'est juste pour éviter des crash +end + +return Menu diff --git a/sonic-radiance.love/birb/modules/gui/menus/views/view1D.lua b/sonic-radiance.love/birb/modules/gui/menus/views/view1D.lua new file mode 100644 index 0000000..563c602 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/views/view1D.lua @@ -0,0 +1,49 @@ +-- view1D.lua : A basic 1D view for menus. +-- Must be used as a subobject of a Menu + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local View1D = Object:extend() + +function View1D:new(slotNumber) + self.slotNumber = slotNumber + self.firstSlot = 1 +end + +function View1D:reset() + self.firstSlot = 1 + print("reset") +end + +function View1D:updateFirstSlot(widgetID) + self.firstSlot = math.max(1, widgetID - (self.slotNumber - 1), math.min(widgetID, self.firstSlot)) +end + +function View1D:isBeforeView(x) + return (x < self.firstSlot) +end + +function View1D:isAfterView(x) + return (x >= (self.firstSlot + self.slotNumber)) +end + +return View1D \ No newline at end of file diff --git a/sonic-radiance.love/birb/modules/gui/menus/views/view2D.lua b/sonic-radiance.love/birb/modules/gui/menus/views/view2D.lua new file mode 100644 index 0000000..feecf25 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/views/view2D.lua @@ -0,0 +1,61 @@ +-- view2D.lua : A basic 2D view for menus. +-- Must be used as a subobject of a Menu + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] +local View1D = require "birb.modules.gui.menus.views.view1D" +local View2D = View1D:extend() + +function View2D:new(colNumber, lineNumber) + self.colNumber = colNumber + self.lineNumber = lineNumber + View2D.super.new(self, (colNumber * lineNumber)) +end + +function View2D:updateFirstSlot(widgetID) + local _, line = self:getCoord(widgetID) + local _, beginline = self:getCoord(self.firstSlot) + + beginline = math.min(line, math.max(0, beginline, line - (self.lineNumber - 1))) + self.view.firstSlot = (beginline * self.view.colNumber) + 1 +end + +-- INFO FUNCTIONS +-- Get informations + +function View2D:getCoord(x) + -- On simplifie les calcul en prenant 0 comme départ + local x = x - 1 + + local line, col + line = math.floor(x / self.view.colNumber) + col = x - (line * self.view.colNumber) + + return col, line +end + +function View2D:getFromCoord(col, line) + local _, beginline = self.view:getCoord(self.view.firstSlot) + local line = beginline + line + return (line * self.view.colNumber) + col + 1 +end + +return View2D diff --git a/sonic-radiance.love/birb/modules/gui/menus/widgets/base.lua b/sonic-radiance.love/birb/modules/gui/menus/widgets/base.lua new file mode 100644 index 0000000..6a72cae --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/widgets/base.lua @@ -0,0 +1,156 @@ +-- widgets/base.lua :: basic widget object + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] +local BaseWidget = Object:extend() + +-- INIT FUNCTIONS +-- Initialize and configure the widget + +function BaseWidget:new(menuName) + self.scene = self:getScene() + self.menu = self:getMenuByName(menuName) + self.assets = self:getAssets() + + self.destroyed = false + self.selectable = false + self.selection_margin = 0 + self.margin = 2 + + self.canvas = {} + self.canvas.texture = nil + self.canvas.needRedraw = true + + self.order = 0 + self:register() +end + +function BaseWidget:getMenuByName(name) + local gui = self:getGui() + assert(name ~= nil, "Name cant be nil") + return gui.elements[name] +end + +function BaseWidget:getScene() + return core.scenemanager.nextScene or core.scenemanager.currentScene +end + +function BaseWidget:getGui() + local scene = core.scenemanager.nextScene or core.scenemanager.currentScene + return scene.gui +end + +function BaseWidget:getAssets() + local scene = core.scenemanager.nextScene or core.scenemanager.currentScene + return scene.assets +end + +function BaseWidget:register() + self.creationID = self.menu:getWidgetNumber() + self.menu:addWidget(self) +end + +function BaseWidget:redrawCanvas() + self.width, self.height = self.menu:getWidgetSize(self.id) + + local canvas = love.graphics.newCanvas(self.width, self.height) + love.graphics.setCanvas(canvas) + + self:drawCanvas() + self.canvas.needRedraw = false + + love.graphics.setCanvas() + local imageData = canvas:newImageData() + self.canvas.texture = love.graphics.newImage(imageData) + canvas:release() + imageData:release() +end + +function BaseWidget:invalidateCanvas() + self.canvas.needRedraw = true +end + +function BaseWidget:drawCanvas() + self.r = love.math.random(128) / 256 + self.g = love.math.random(128) / 256 + self.b = love.math.random(128) / 256 + + love.graphics.setColor(self.r, self.g, self.b, 70) + love.graphics.rectangle("fill", 0, 0, self.width, self.height) + love.graphics.setColor(self.r, self.g, self.b) + love.graphics.rectangle("line", 0, 0, self.width, self.height) + utils.graphics.resetColor() +end + +function BaseWidget:selectAction() + -- Do nothing +end + +-- DRAW WIDGETS +-- Draw the widget + +function BaseWidget:drawWidget(x, y, w, h) + if (self:haveFocus()) then + self:drawSelected(x, y, w, h) + else + self:draw(x, y, w, h) + end +end + +function BaseWidget:draw(x, y, w, h) + if self.canvas.texture ~= nil then + love.graphics.draw(self.canvas.texture, x, y) + end +end + +function BaseWidget:drawSelected(x, y, w, h) + self:draw(x, y, w, h) +end + +-- UPDATE FUNCTIONS +-- Update the widget + +function BaseWidget:update(dt) + if (self.canvas.needRedraw) then + self:redrawCanvas() + end + -- N/A +end + +-- FOCUS FUNCTIONS +-- Detect if the widget have focus + +function BaseWidget:haveFocus() + return (self.menu:haveFocus() and self.menu.widget:getSelected() == self.id) +end + +-- ACTION FUNCTION +-- Functions to handle actions and selection. + +function BaseWidget:action(source) + --self:destroy() +end + +function BaseWidget:destroy() + self.destroyed = true +end + +return BaseWidget diff --git a/sonic-radiance.love/birb/modules/gui/menus/widgets/init.lua b/sonic-radiance.love/birb/modules/gui/menus/widgets/init.lua new file mode 100644 index 0000000..0c8c4e8 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/widgets/init.lua @@ -0,0 +1,30 @@ +-- widgets :: basic widget object + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local Widget = {} + +-- Add the widget as subvariable to the returned table +Widget.Base = require "birb.modules.gui.menus.widgets.base" +Widget.Text = require "birb.modules.gui.menus.widgets.text" + +return Widget diff --git a/sonic-radiance.love/birb/modules/gui/menus/widgets/text.lua b/sonic-radiance.love/birb/modules/gui/menus/widgets/text.lua new file mode 100644 index 0000000..f8f9465 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/menus/widgets/text.lua @@ -0,0 +1,95 @@ +-- widgets/text.lua :: basic text widget object + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] +local BaseWidget = require "birb.modules.gui.menus.widgets.base" +local TextWidget = BaseWidget:extend() + +-- TEXT WIDGET +-- Simple text widget + +function TextWidget:new(menuName, font, label, position, padding) + TextWidget.super.new(self, menuName) + self.font = font + self.labels = {} + self.padding = padding or 0 + + -- We add the first label + local position = position or "center" + self:addLabel(label, position) + self:setSelectedColor(1, 1, 1) +end + +function TextWidget:addLabel(label, position) + local complexLabel = {} + complexLabel.label = label + complexLabel.position = position + table.insert(self.labels, complexLabel) +end + +function TextWidget:replaceLabel(id, newLabel) + self.labels[id].label = newLabel +end + +function TextWidget:setSelectedColor(r, g, b) + self.selectedColor = {} + self.selectedColor.r = r + self.selectedColor.g = g + self.selectedColor.b = b +end + +function TextWidget:getFont() + return self.assets:getFont(self.font) +end + +function TextWidget:getSelectedColor() + return self.selectedColor.r, self.selectedColor.g, self.selectedColor.b +end + +function TextWidget:getPadding() + return self.padding +end + +function TextWidget:drawCanvas() + local w, h + local font = self:getFont() + h = math.floor(self.height / 2) - (font:getHeight() / 2) + + for _, complexLabel in pairs(self.labels) do + if (complexLabel.position == "center") then + w = math.floor(self.width / 2) + elseif (complexLabel.position == "left") then + w = self:getPadding() + elseif (complexLabel.position == "right") then + w = math.floor(self.width - self:getPadding()) + end + font:draw(complexLabel.label, w, h, -1, complexLabel.position) + end +end + +function TextWidget:drawSelected(x, y, w, h) + local r, g, b = self:getSelectedColor() + love.graphics.setColor(r, g, b, 1) + self:draw(x, y) + utils.graphics.resetColor() +end + +return TextWidget diff --git a/sonic-radiance.love/birb/modules/gui/mixins/elements.lua b/sonic-radiance.love/birb/modules/gui/mixins/elements.lua index e2dfb10..8afa4c9 100644 --- a/sonic-radiance.love/birb/modules/gui/mixins/elements.lua +++ b/sonic-radiance.love/birb/modules/gui/mixins/elements.lua @@ -16,6 +16,12 @@ function ElementList:deleteElement(name) self.elements[name] = nil end +function ElementList:setFocus(name) + assert(self:elementExists(name), "Element " .. name .. " doesn't exists") + self.focusedElement = name + self.elements[name].isVisible = true +end + function ElementList:elementExists(name) return (self:getElement(name) ~= nil) end diff --git a/sonic-radiance.love/birb/modules/gui/textmenu/init.lua b/sonic-radiance.love/birb/modules/gui/textmenu/init.lua new file mode 100644 index 0000000..e36f8cf --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/textmenu/init.lua @@ -0,0 +1,78 @@ +-- listbox : a text-based menu. +-- It allow to easily handle a listbox with text-widget and automatically add a widget +-- for submenu. +-- It also make the menu manage the font and the selection color, for a simpler handling + +--[[ + Copyright © 2020 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local ListBox = require "birb.modules.gui.menus.listbox" +local TextMenu = ListBox:extend() + +TextMenu.baseWidgets = require "birb.modules.gui.textmenu.widgets" + +local BASE_PADDING = 8 + +function TextMenu:new(name, font, x, y, w, slotNumber, padding) + self:setFont(font) + self.name = name + local h = self.font:getHeight() * slotNumber + self.padding = padding or BASE_PADDING + self:setSelectedColor(1,1,1) + TextMenu.super.new(self, name, x, y, w, h, slotNumber) +end + +function TextMenu:setFont(fontName) + local scene = core.scenemanager.currentScene + self.font = scene.assets:getFont(fontName) +end + +function TextMenu:getFont() + return self.font +end + +function TextMenu:setSelectedColor(r, g, b) + self.selectedColor = {} + self.selectedColor.r = r + self.selectedColor.g = g + self.selectedColor.b = b +end + +function TextMenu:getSelectedColor() + return self.selectedColor.r, self.selectedColor.g, self.selectedColor.b +end + +function TextMenu:getPadding() + return self.padding +end + +function TextMenu:addSubmenu(pageName, label, parent, backWidget) + local label = label or pageName + local parent = parent or "main" + self:switch(parent) + TextMenu.baseWidgets.SubMenu(self.name, pageName, label) + TextMenu.super.addSubmenu(self, pageName, parent) + if (backWidget ~= false) then + TextMenu.baseWidgets.Back(self.name) + end +end + +return TextMenu \ No newline at end of file diff --git a/sonic-radiance.love/birb/modules/gui/textmenu/widgets/back.lua b/sonic-radiance.love/birb/modules/gui/textmenu/widgets/back.lua new file mode 100644 index 0000000..ad053dd --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/textmenu/widgets/back.lua @@ -0,0 +1,37 @@ +-- backwidget : the base widget to go to parent submenu + +--[[ + Copyright © 2020 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local TextMenuWidget = require "birb.modules.gui.textmenu.widgets.basic" + +local BackWidget = TextMenuWidget:extend() + +function BackWidget:new(menuName) + BackWidget.super.new(self, menuName, "Back", "left") + self.order = 1000 +end + +function BackWidget:action() + self.menu:back() +end + +return BackWidget \ No newline at end of file diff --git a/sonic-radiance.love/birb/modules/gui/textmenu/widgets/basic.lua b/sonic-radiance.love/birb/modules/gui/textmenu/widgets/basic.lua new file mode 100644 index 0000000..9544270 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/textmenu/widgets/basic.lua @@ -0,0 +1,47 @@ +-- widget : the base widget for a textmenu +-- It replace the functions to get the font and the selectionColor by wrapper to +-- TextMenu. + +--[[ + Copyright © 2020 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local TextWidget = require "birb.modules.gui.menus.widgets.text" + +local TextMenuWidget = TextWidget:extend() + +function TextMenuWidget:new(menuName, label, position) + local position = position or "left" + TextMenuWidget.super.new(self, menuName, "", label, position, 0) +end + +function TextMenuWidget:getFont() + return self.menu:getFont() +end + +function TextMenuWidget:getSelectedColor() + return self.menu:getSelectedColor() +end + +function TextMenuWidget:getPadding() + return self.menu:getPadding() +end + +return TextMenuWidget \ No newline at end of file diff --git a/sonic-radiance.love/birb/modules/gui/textmenu/widgets/init.lua b/sonic-radiance.love/birb/modules/gui/textmenu/widgets/init.lua new file mode 100644 index 0000000..5c5e761 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/textmenu/widgets/init.lua @@ -0,0 +1,31 @@ +-- widgets :: basic widget object + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local Widget = {} + +-- Add the widget as subvariable to the returned table +Widget.Base = require "birb.modules.gui.textmenu.widgets.basic" +Widget.SubMenu= require "birb.modules.gui.textmenu.widgets.submenu" +Widget.Back = require "birb.modules.gui.textmenu.widgets.back" + +return Widget diff --git a/sonic-radiance.love/birb/modules/gui/textmenu/widgets/submenu.lua b/sonic-radiance.love/birb/modules/gui/textmenu/widgets/submenu.lua new file mode 100644 index 0000000..a38d390 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/textmenu/widgets/submenu.lua @@ -0,0 +1,39 @@ +-- widget : the base widget to go to a submenu +-- Make go to a submenu + +--[[ + Copyright © 2020 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local TextMenuWidget = require "birb.modules.gui.textmenu.widgets.basic" + +local SubmenuWidget = TextMenuWidget:extend() + +function SubmenuWidget:new(menuName, submenu, label, position) + self.submenu = submenu + SubmenuWidget.super.new(self, menuName, label, position) + self:addLabel(">", "right") +end + +function SubmenuWidget:action() + self.menu:switch(self.submenu) +end + +return SubmenuWidget \ No newline at end of file diff --git a/sonic-radiance.love/birb/modules/gui/utils.lua b/sonic-radiance.love/birb/modules/gui/utils.lua new file mode 100644 index 0000000..a8c73e6 --- /dev/null +++ b/sonic-radiance.love/birb/modules/gui/utils.lua @@ -0,0 +1,57 @@ +-- menusystem/utils :: basic utility functions for the gui + +--[[ + Copyright © 2019 Kazhnuz + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +local guiUtils = {} + +function guiUtils.drawCursor(x, y, w, h) + love.graphics.setColor(0,0,0) + + love.graphics.rectangle("fill", x, y, 4, 8) + love.graphics.rectangle("fill", x, y, 8, 4) + + love.graphics.rectangle("fill", x + w, y, -4, 8) + love.graphics.rectangle("fill", x + w, y, -8, 4) + + love.graphics.rectangle("fill", x, y + h, 4, -8) + love.graphics.rectangle("fill", x, y + h, 8, -4) + + love.graphics.rectangle("fill", x + w, y + h, -4, -8) + love.graphics.rectangle("fill", x + w, y + h, -8, -4) + + love.graphics.setColor(255,255,255) + + love.graphics.rectangle("fill", x + 1, y + 1, 2, 6) + love.graphics.rectangle("fill", x + 1, y + 1, 6, 2) + + love.graphics.rectangle("fill", x + w - 1, y + 1, -2, 6) + love.graphics.rectangle("fill", x + w - 1, y + 1, -6, 2) + + love.graphics.rectangle("fill", x + 1, y + h - 1, 2, -6) + love.graphics.rectangle("fill", x + 1, y + h - 1, 6, -2) + + love.graphics.rectangle("fill", x + w - 1, y + h - 1, -2, -6) + love.graphics.rectangle("fill", x + w - 1, y + h - 1, -6, -2) + +end + +return guiUtils