diff --git a/scripts/common/filereader.lua b/scripts/common/filereader.lua index c450b93..3cd12f5 100644 --- a/scripts/common/filereader.lua +++ b/scripts/common/filereader.lua @@ -40,8 +40,8 @@ function ReadGameFileLines(path, mode) end ---Read a file in the game folder ----@param path string ----@param mode? openmode default "r" +---@param path string # relative path to game file +---@param mode? openmode # default "r" ---@return nil|string|integer[] function ReadGameFile(path, mode) mode = mode or "r" @@ -63,9 +63,9 @@ function ReadGameFile(path, mode) end ---Find patterns in file ----@param path string ----@param pattern string ----@return table {{group1, group2, ...}, ...} +---@param path string # relative path to game file +---@param pattern string # search pattern +---@return table # {{group1, group2, ...}, ...} function FindPatterns(path, pattern) local matches = {} for _, line in ipairs(ReadGameFileLines(path, "r")) do @@ -76,3 +76,28 @@ function FindPatterns(path, pattern) return matches end +--- Check if a file or directory exists in this path +---@param file string # relative path to game file +---@return boolean # file exists +---@return string # error message +function IsFileExists(file) + local gamepath, sep = getGamePath() + file = gamepath .. sep .. file + + local ok, err, code = os.rename(file, file) + if not ok then + if code == 13 then + -- Permission denied, but it exists + return true + end + end + return ok, err +end + +--- Check if a directory exists in this path +---@param path string # relative path to game directory +---@return boolean # directory exists +function IsDir(path) + -- "/" works on both Unix and Windows + return IsFileExists(path .. "/") +end diff --git a/scripts/components/pager/containerfield.lua b/scripts/components/pager/containerfield.lua index 7c8b2b3..12f015a 100644 --- a/scripts/components/pager/containerfield.lua +++ b/scripts/components/pager/containerfield.lua @@ -17,15 +17,11 @@ function ContainerField:new(o) o.content = o.content or {} - return Inherit(self, o, Field) -end + local this = Inherit(self, o, Field) ----Initialize members ----@return ContainerField -function ContainerField:init() - Field.init(self) - self:refreshFields() - return self + this:refreshFields() + + return this end ---Add content to container @@ -60,7 +56,7 @@ function ContainerField:render(deltaTime) gfx.Save() gfx.Translate(self.posX, self.posY) - gfx.Scissor(self.offX, self.offY, self.aabbW, self.aabbH) + gfx.Scissor(0, 0, self.aabbW, self.aabbH) self:drawBackground(deltaTime) self:drawContent(deltaTime) diff --git a/scripts/components/pager/field.lua b/scripts/components/pager/field.lua index e9dd005..a97ff47 100644 --- a/scripts/components/pager/field.lua +++ b/scripts/components/pager/field.lua @@ -4,18 +4,10 @@ require("common.class") ---@field parent Page|ContainerField ---@field posX number ---@field posY number ----@field offX number ----@field offY number ---@field aabbW number ---@field aabbH number local Field = { __tostring = function() return "Field" end, - ---@type nil|fun(button: integer): boolean - ---return true if further button input processing should be stopped, otherwise false - handleButtonInput = nil, - ---@type nil|fun(knob: integer, delta: number): boolean - ---return true if further knob input processing should be stopped, otherwise false - handleKnobInput = nil, } ---Create a new Field instance @@ -29,20 +21,12 @@ function Field:new(o) o.parent = o.parent or nil o.posX = o.posX or 0 o.posY = o.posY or 0 - o.offX = o.offX or 0 - o.offY = o.offY or 0 o.aabbW = o.aabbW or 0 o.aabbH = o.aabbH or 0 return Base(self, o) end ----Initialize members ----@return Field -function Field:init() - return self -end - ---Get the containing top-level parent page ---@return Field|Page function Field:getParentPage() @@ -62,8 +46,23 @@ function Field:focus(obj) end ---@param obj? any # message object for the field function Field:deactivate(obj) end +---@param button integer # options are under the `game` table prefixed with `BUTTON` +---@return boolean # true if further button input processing should be stopped, otherwise false +function Field:handleButtonInput(button) + return false +end + +---@param knob integer # `0` = Left, `1` = Right +---@param delta number # in radians, `-2*pi` to `0` (turning CCW) and `0` to `2*pi` (turning CW) +---@return boolean # true if further button input processing should be stopped, otherwise false +function Field:handleKnobInput(knob, delta) + return false +end + ---@param deltaTime number # frametime in seconds function Field:drawContent(deltaTime) + -- dummy field content + gfx.ResetScissor() local offX = -50 @@ -104,7 +103,8 @@ function Field:render(deltaTime) gfx.Save() gfx.Translate(self.posX, self.posY) - gfx.Scissor(self.offX, self.offY, self.aabbW, self.aabbH) + gfx.Scissor(0, 0, self.aabbW, self.aabbH) + self:drawContent(deltaTime) gfx.Restore() diff --git a/scripts/components/pager/page.lua b/scripts/components/pager/page.lua index 91304d4..43edab4 100644 --- a/scripts/components/pager/page.lua +++ b/scripts/components/pager/page.lua @@ -5,9 +5,6 @@ require("common.class") ---@field viewHandler nil|PageView local Page = { __tostring = function() return "Page" end, - drawBackground = nil, ---@type nil|fun(deltaTime: number) - drawHeader = nil, ---@type nil|fun(deltaTime: number) - drawFooter = nil, ---@type nil|fun(deltaTime: number) } ---Create a new Page instance @@ -24,12 +21,6 @@ function Page:new(o) return Base(self, o) end ----Initialize members ----@return Page -function Page:init() - return self -end - ---Add field to page ---@param field Field function Page:addField(field) @@ -58,26 +49,23 @@ end function Page:handleKnobInput(knob, delta) end ---@param deltaTime number # frametime in seconds -function Page:render(deltaTime) - ---background - if self.drawBackground then - self:drawBackground(deltaTime) - end +function Page:drawBackground(deltaTime) end - --render children +---@param deltaTime number # frametime in seconds +function Page:drawContent(deltaTime) for _, child in ipairs(self.content) do child:render(deltaTime) end +end - ---header - if self.drawHeader then - self:drawHeader(deltaTime) - end +---@param deltaTime number # frametime in seconds +function Page:drawForeground(deltaTime) end - ---footer - if self.drawFooter then - self:drawFooter(deltaTime) - end +---@param deltaTime number # frametime in seconds +function Page:render(deltaTime) + self:drawBackground(deltaTime) + self:drawContent(deltaTime) + self:drawForeground(deltaTime) end return Page diff --git a/scripts/titlescreen.lua b/scripts/titlescreen.lua index ea538eb..d727a57 100644 --- a/scripts/titlescreen.lua +++ b/scripts/titlescreen.lua @@ -24,7 +24,7 @@ local screens = { } } -local currentScreen = game.GetSkinSetting("animations_skipIntro") and screens.title or screens.service -- show boot screen if skipIntro is not set +local currentScreen = game.GetSkinSetting("animations_skipIntro") and screens.title or screens.boot -- show boot screen if skipIntro is not set local function deltaKnob(delta) if math.abs(delta) > 1.5 * math.pi then diff --git a/scripts/titlescreen/boot.lua b/scripts/titlescreen/boot.lua index 8ad3a48..4cfe557 100644 --- a/scripts/titlescreen/boot.lua +++ b/scripts/titlescreen/boot.lua @@ -14,8 +14,15 @@ local function render(deltaTime) Dim.transformToScreenSpace() pageview:render(deltaTime) + + --pageview will be empty when you `back()` out of the root page + if not pageview:get() then + return {eventType = "switch", toScreen = "splash"} + end end -return { - render = render -} \ No newline at end of file +local function onButtonPressed(button) + pageview:get():handleButtonInput(button) +end + +return {render = render, onButtonPressed = onButtonPressed} \ No newline at end of file diff --git a/scripts/titlescreen/fields/boot/selftestfield.lua b/scripts/titlescreen/fields/boot/selftestfield.lua index abc8bec..7a38175 100644 --- a/scripts/titlescreen/fields/boot/selftestfield.lua +++ b/scripts/titlescreen/fields/boot/selftestfield.lua @@ -13,35 +13,45 @@ SelfTestStatusEnum = { ---@class SelfTestField: ServiceField ---@field status SelfTestStatusEnum +---@field callback nil|fun(obj: any): SelfTestStatusEnum ---@field _timer number local SelfTestField = { SELFTEST_COLOR_INPROGRESS = {255, 255, 255, 255}, SELFTEST_COLOR_OK = {0, 255, 0, 255}, SELFTEST_COLOR_PASS = {255, 255, 0, 255}, SELFTEST_COLOR_ERROR = {255, 0, 0, 255}, - SELFTEST_INPROGRESS_FREQ = 1 / 20 --20Hz + SELFTEST_INPROGRESS_FREQ = 1 / 20, --20Hz } ---Create a new SelfTestField instance ----@param o ServiceField +---@param o? table ---@return SelfTestField function SelfTestField:new(o) o = o or {} o.status = o.status or SelfTestStatusEnum.IDLE + o.callback = o.callback or nil o._timer = 0 return Inherit(self, o, ServiceField) end +function SelfTestField:activate(obj) + if self.callback then + self.status = self.callback(obj) or SelfTestStatusEnum.INPROGRESS + end +end + function SelfTestField:drawValue(deltaTime) + gfx.Translate(self.VALUE_OFFSETX, 0) + gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT | gfx.TEXT_ALIGN_TOP) - gfx.FillColor(table.unpack(self.SERVICE_DEFAULT_FONT_COLOR)) - gfx.Text(":", self.valueOffX, 0) + gfx.FillColor(table.unpack(self.FONT_COLOR)) + gfx.Text(": ", 0, 0) local color, text if self.status == SelfTestStatusEnum.IDLE then - color = self.SERVICE_DEFAULT_FONT_COLOR + color = self.FONT_COLOR text = "" elseif self.status == SelfTestStatusEnum.INPROGRESS then self._timer = self._timer + deltaTime @@ -58,9 +68,10 @@ function SelfTestField:drawValue(deltaTime) color = self.SELFTEST_COLOR_ERROR text = "ERROR" end + gfx.TextAlign(gfx.TEXT_ALIGN_LEFT | gfx.TEXT_ALIGN_TOP) gfx.FillColor(table.unpack(color)) - gfx.Text(text, self.valueOffX, 0) + gfx.Text(text, 0, 0) end return SelfTestField \ No newline at end of file diff --git a/scripts/titlescreen/fields/service/colorgradientfield.lua b/scripts/titlescreen/fields/service/colorgradientfield.lua index b22c387..c242b3a 100644 --- a/scripts/titlescreen/fields/service/colorgradientfield.lua +++ b/scripts/titlescreen/fields/service/colorgradientfield.lua @@ -6,7 +6,7 @@ local ServiceField = require("titlescreen.fields.service.servicefield") local ColorGradientField = { __tostring = function() return "ColorGradientField" end, GRADIENT_X_OFFSET = 128, - GRADIENT_WIDTH = 512, + GRADIENT_WIDTH = 576, GRADIENT_STEPS = 32 } @@ -17,7 +17,6 @@ function ColorGradientField:new(o) o = o or {} o.value = o.value or {0, 0, 0, 255} - o.aabbH = o.aabbH or self.FONT_SIZE return Inherit(self, o, ServiceField) end diff --git a/scripts/titlescreen/fields/service/inputbuttonfield.lua b/scripts/titlescreen/fields/service/inputbuttonfield.lua index 6adba48..c8f1571 100644 --- a/scripts/titlescreen/fields/service/inputbuttonfield.lua +++ b/scripts/titlescreen/fields/service/inputbuttonfield.lua @@ -29,13 +29,15 @@ function InputButtonField:deactivate(obj) end ---@param deltaTime number # frametime in seconds function InputButtonField:drawValue(deltaTime) + gfx.Translate(self.VALUE_OFFSETX, 0) + if not self.button then - gfx.Text("