tldr; bootpage initial implementation, fixes for page management code

added supporting functions for BootPage
removed init() from classes, it was stupid to begin with
handleButtonInput() and handleKnobInput no longer optional
added callback member to SelfTestField
placement position value fixes
use gfx.Translate instead of explicitly setting off-origin x,y values
added MARGIN and PADDING calculations to fields
removed unnecessary gfx.BeginPath() calls
This commit is contained in:
Hersi 2022-04-15 04:54:30 +02:00
parent 868d5b9798
commit f44ba3743b
22 changed files with 385 additions and 327 deletions

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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
}
local function onButtonPressed(button)
pageview:get():handleButtonInput(button)
end
return {render = render, onButtonPressed = onButtonPressed}

View File

@ -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

View File

@ -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

View File

@ -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("<BUTTON NOT SET>", self.valueOffX, 0)
gfx.Text("<BUTTON NOT SET>", 0, 0)
return
end
self.value = game.GetButton(self.button) and "ON" or "OFF"
gfx.Text(self.value, self.valueOffX, 0)
gfx.Text(self.value, 0, 0)
end
return InputButtonField

View File

@ -6,7 +6,7 @@ local ServiceField = require("titlescreen.fields.service.servicefield")
---@field knob integer
local InputKnobField = {
__tostring = function() return "InputKnobField" end,
SLIDER_SIZE = {256, ServiceField.FONT_SIZE}, --{w, h}
SLIDER_SIZE = {200, 16}, --{w, h}
SLIDER_BGCOLOR = {255, 0, 0, 255},
SLIDER_FRAME_COLOR = ServiceField.FONT_COLOR,
SLIDER_FRAME_WIDTH = 1,
@ -37,13 +37,14 @@ function InputKnobField:deactivate(obj) end
---@param deltaTime number # frametime in seconds
function InputKnobField:drawValue(deltaTime)
gfx.Translate(self.VALUE_OFFSETX, 0)
if not self.knob then
gfx.Text("<KNOB NOT SET>", self.valueOffX, 0)
gfx.Text("<KNOB NOT SET>", 0, 0)
return
end
local knobAngle = game.GetKnob(self.knob)
local sliderPosX = self.valueOffX + self.SLIDER_OFFSETX
local sliderWidth = self.SLIDER_SIZE[1]
local sliderHeight = self.SLIDER_SIZE[2]
local sliderBgColor = self.SLIDER_BGCOLOR
@ -54,19 +55,19 @@ function InputKnobField:drawValue(deltaTime)
self.value = math.floor(Util.lerp(knobAngle,0, 0, 2 * math.pi, maxValue)) % maxValue
--draw value
gfx.BeginPath()
gfx.Text(self.value, self.valueOffX, 0)
gfx.Text(self.value, 0, 0)
--draw slider
gfx.Translate(self.SLIDER_OFFSETX, 0)
gfx.BeginPath()
gfx.Rect(sliderPosX, 0, sliderWidth, sliderHeight)
gfx.Rect(0, 0, sliderWidth, sliderHeight)
gfx.FillColor(table.unpack(sliderBgColor))
gfx.StrokeColor(table.unpack(sliderFrameColor))
gfx.StrokeWidth(sliderFrameWidth)
gfx.Fill()
gfx.Stroke()
local sliderIndicatorX = Util.lerp(self.value, 0, sliderPosX, maxValue, sliderWidth)
local sliderIndicatorX = Util.lerp(self.value, 0, 0, maxValue, sliderWidth)
local sliderIndicatorWidth = self.SLIDER_INDICATOR_WIDTH
local sliderIndicatorColor = self.SLIDER_INDICATOR_COLOR
--draw indicator
@ -74,7 +75,6 @@ function InputKnobField:drawValue(deltaTime)
gfx.Rect(sliderIndicatorX, sliderFrameWidth, sliderIndicatorWidth, sliderHeight - 2 * sliderFrameWidth)
gfx.FillColor(table.unpack(sliderIndicatorColor))
gfx.Fill()
end
return InputKnobField

View File

@ -4,8 +4,11 @@ local ServiceField = require("titlescreen.fields.service.servicefield")
---@class ListField: ContainerField, ServiceField
---@field selectedIndex integer
---@field locked boolean
---@field PADDING number[]
local ListField = {
__tostring = function() return "ListField" end,
MARGIN = {0, 0, 0, 0}, --{left, top, right, bottom}
PADDING = {0, 0, 0, 0}, --{left, top, right, bottom}
}
@ -17,20 +20,16 @@ function ListField:new(o)
--set instance members
o.selectedIndex = o.selectedIndex or 1
o.locked = o.locked or false
return Inherit(self, o, ContainerField, ServiceField)
end
local this = Inherit(self, o, ContainerField, ServiceField)
---Initialize members
---@return ListField
function ListField:init()
ServiceField.init(self)
ContainerField.init(self)
local minW = self.MARGIN[1] + self.PADDING[1] + self.PADDING[3] + self.MARGIN[3]
local minH = self.MARGIN[2] + self.PADDING[2] + self.PADDING[4] + self.MARGIN[4]
self.aabbW = math.max(self.aabbW, minW)
self.aabbH = math.max(self.aabbH, minH)
return self
local minW = this.MARGIN[1] + this.PADDING[1] + this.PADDING[3] + this.MARGIN[3]
local minH = this.MARGIN[2] + this.PADDING[2] + this.PADDING[4] + this.MARGIN[4]
this.aabbW = math.max(this.aabbW, minW)
this.aabbH = math.max(this.aabbH, minH)
return this
end
---@param obj? any # message object for the field
@ -66,10 +65,6 @@ end
---Add field to list container
---@param field Field
function ListField:addField(field)
--place field to correct position
field.posX = self.PADDING[1]
field.posY = self.aabbH - self.PADDING[4]
--update size
self.aabbH = self.aabbH + field.aabbH
local fieldAabbW = self.PADDING[1] + field.aabbW + self.PADDING[3]
@ -83,14 +78,8 @@ end
---Refresh content parameters
function ListField:refreshFields()
local posY = self.PADDING[1]
local aabbH = self.MARGIN[2] + self.PADDING[2] + self.PADDING[4] + self.MARGIN[4]
for _, child in ipairs(self.content) do
--place field to correct position
child.posX = self.PADDING[1]
child.posY = posY
posY = posY + child.aabbH
--update size
aabbH = aabbH + child.aabbH
local fieldAabbW = self.PADDING[1] + child.aabbW + self.PADDING[3]
@ -107,20 +96,25 @@ end
---@return boolean # true if further button input processing should be stopped, otherwise false
function ListField:handleButtonInput(button)
local field = self.content[self.selectedIndex]
if field and field.handleButtonInput then
if field:handleButtonInput(button) then
return true
end
end
local direction = 0
if button == game.BUTTON_BCK then
local viewHandler = self:getParentPage().viewHandler
if viewHandler then
viewHandler:back()
end
elseif button == game.BUTTON_BTA then
return true
end
if self.locked then
return true
end
local direction = 0
if button == game.BUTTON_BTA then
direction = -1
elseif button == game.BUTTON_BTB then
direction = 1
@ -142,4 +136,13 @@ function ListField:handleButtonInput(button)
return true
end
---@param deltaTime number # frametime in seconds
function ListField:drawContent(deltaTime)
gfx.Translate(self.PADDING[1] + self.MARGIN[1], self.PADDING[2] + self.MARGIN[2])
for _, child in ipairs(self.content) do
child:render(deltaTime)
gfx.Translate(0, child.aabbH)
end
end
return ListField

View File

@ -12,17 +12,24 @@ ServiceFieldState = {
---@class ServiceField: Field
---@field label string
---@field value any
---@field valueOffX number
---@field footer string|string[]
---@field _state ServiceFieldState
---@field FONT_SIZE number
---@field FONT_FACE string
---@field FONT_COLOR integer[] # {r, g, b, a}
---@field FONT_ACTIVE_COLOR integer[] # {r, g, b, a}
---@field FONT_FOCUSED_COLOR integer[] # {r, g, b, a}
---@field MARGIN number[] # {left, top, right, bottom}
---@field VALUE_OFFSETX number
local ServiceField = {
__tostring = function() return "ServiceField" end,
FONT_SIZE = 24,
FONT_FACE = "dfmarugoth.ttf",
FONT_COLOR = {255, 255, 255, 255}, --{r, g, b, a}
FONT_COLOR = {255, 255, 255, 255},
FONT_ACTIVE_COLOR = {0, 255, 0, 255},
FONT_FOCUSED_COLOR = {255, 0, 0, 255},
MARGIN = {0, 0, 0, 4}, --{left, top, right, bottom}
MARGIN = {0, 0, 0, 0},
VALUE_OFFSETX = 500
}
---Create a new ServiceField instance
@ -31,14 +38,10 @@ local ServiceField = {
function ServiceField:new(o)
o = o or {}
local h = self.FONT_SIZE +
self.MARGIN[2] +
self.MARGIN[4]
local h = self.FONT_SIZE + self.MARGIN[2] + self.MARGIN[4]
o.aabbH = o.aabbH or h
o.aabbW = o.aabbW or Dim.design.width - 128 --:shrug:
o.valueOffX = o.valueOffX or Dim.design.width / 2
o.aabbW = o.aabbW or Dim.design.width --:shrug:
o.label = o.label or "<UNDEFINED>"
o.value = o.value or nil
@ -46,25 +49,31 @@ function ServiceField:new(o)
o._state = ServiceFieldState.INACTIVE
return Inherit(self, o, Field)
local this = Inherit(self, o, Field)
if this.aabbH < h then
this.aabbH = h
end
return this
end
---@param obj? any message object for the field
---@param obj? any # message object for the field
function ServiceField:activate(obj)
self._state = ServiceFieldState.ACTIVE
end
---@param obj? any message object for the field
---@param obj? any # message object for the field
function ServiceField:focus(obj)
self._state = ServiceFieldState.FOCUSED
end
---@param obj? any message object for the field
---@param obj? any # message object for the field
function ServiceField:deactivate(obj)
self._state = ServiceFieldState.INACTIVE
end
---@param deltaTime number frametime in seconds
---@param deltaTime number # frametime in seconds
function ServiceField:drawLabel(deltaTime)
local color
if self._state == ServiceFieldState.FOCUSED then
@ -80,7 +89,7 @@ function ServiceField:drawLabel(deltaTime)
gfx.Text(self.label, 0, 0)
end
---@param deltaTime number frametime in seconds
---@param deltaTime number # frametime in seconds
function ServiceField:drawValue(deltaTime)
local text
if type(self.value) == "string" then
@ -89,15 +98,18 @@ function ServiceField:drawValue(deltaTime)
text = "N/A"
end
gfx.Translate(self.VALUE_OFFSETX, 0)
gfx.FontSize(self.FONT_SIZE)
gfx.LoadSkinFont(self.FONT_FACE)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT | gfx.TEXT_ALIGN_TOP)
gfx.FillColor(table.unpack(self.FONT_COLOR))
gfx.Text(text, self.valueOffX, 0)
gfx.Text(text, 0, 0)
end
---@param deltaTime number frametime in seconds
---@param deltaTime number # frametime in seconds
function ServiceField:drawContent(deltaTime)
gfx.Translate(self.MARGIN[1], self.MARGIN[2])
self:drawLabel(deltaTime)
self:drawValue(deltaTime)
end

View File

@ -19,4 +19,8 @@ end
---@param deltaTime number # frametime in seconds
function ServiceLinkField:drawValue(deltaTime) end
function ServiceLinkField:handleButtonInput(button)
return LinkField.handleButtonInput(self, button)
end
return ServiceLinkField

View File

@ -1,19 +0,0 @@
require("common.class")
local ServiceField = require("titlescreen.fields.service.servicefield")
---@class SpacerField: ServiceField
local SpacerField = {}
---Create a new ServiceField instance
---@param o? table # initial parameters
---@return SpacerField
function SpacerField:new(o)
return Inherit(self, o, ServiceField)
end
---@param deltaTime number # frametime in seconds
function SpacerField:drawContent(deltaTime)
--empty, what did you think would be here?
end
return SpacerField

View File

@ -23,6 +23,15 @@ function UpdateField:new(o)
return Inherit(self, o, ServiceField)
end
---@param obj? any # message object for the field
function UpdateField:activate(obj) end
---@param obj? any # message object for the field
function UpdateField:focus(obj) end
---@param obj? any # message object for the field
function UpdateField: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 UpdateField:handleButtonInput(button)
@ -41,7 +50,8 @@ function UpdateField:drawValue(deltaTime)
self._timer = self._timer + deltaTime
local url, version = game.UpdateAvailable()
gfx.BeginPath()
gfx.Translate(self.VALUE_OFFSETX, 0)
gfx.FontSize(self.FONT_SIZE)
gfx.LoadSkinFont(self.FONT_FACE)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT | gfx.TEXT_ALIGN_TOP)
@ -51,12 +61,11 @@ function UpdateField:drawValue(deltaTime)
else
gfx.FillColor(table.unpack(self.UPDATE_FLICKER_COLORS[2]))
end
gfx.Text("*UPDATE AVAILABLE (" .. version .. ")*", self.valueOffX, 0)
gfx.Text("*UPDATE AVAILABLE (" .. version .. ")*", 0, 0)
else
gfx.FillColor(table.unpack(self.FONT_COLOR))
gfx.Text(self.value or "<VERSION STRING NOT AVAILABLE>", self.valueOffX, 0)
gfx.Text(self.value or "<VERSION STRING NOT AVAILABLE>", 0, 0)
end
end
return UpdateField

View File

@ -1,9 +1,35 @@
require("common.class")
require("common.filereader")
local Dim = require("common.dimensions")
local Version = require("common.version")
local Page = require("components.pager.page")
local ServiceField = require("titlescreen.fields.service.servicefield")
local LinkField = require("components.pager.linkfield")
local ListField = require("titlescreen.fields.service.listfield")
local SelfTestField = require("titlescreen.fields.boot.selftestfield")
local function checkSkinConfig(obj)
local crewpath = "textures/crew/anim/" .. game.GetSkinSetting("single_idol")
if not IsDir(crewpath) then
return SelfTestStatusEnum.ERROR
end
end
local networkStatus = SelfTestStatusEnum.IDLE
local function irHeardbeat(res)
if res.statusCode == IRData.States.Success then
networkStatus = SelfTestStatusEnum.OK
else
networkStatus = SelfTestStatusEnum.ERROR
end
end
local function checkNetwork(obj)
if not IRData.Active then
return SelfTestStatusEnum.PASS
end
IR.Heartbeat(irHeardbeat)
end
---@class BootPage: Page
local BootPage = {
@ -16,27 +42,19 @@ local BootPage = {
function BootPage:new(o)
o = o or {}
return Inherit(self, o, Page)
end
local this = Inherit(self, o, Page)
---Initialize members
---@return BootPage
function BootPage:init()
self:addField(ServiceField:new{
label = Version.getLongVersion(),
value = "",
posX = 32,
posY = 32
})
this:addField(ServiceField:new{posX = 32, posY = 32, label = Version.getLongVersion(), value = ""})
this:addField(ServiceField:new{posX = 64, posY = 64, label = "UNNAMED SDVX CLONE STARTUP...", value = ""})
self:addField(ServiceField:new{
label = "UNNAMED SDVX CLONE STARTUP...",
value = "",
posX = 64,
posY = 64
})
local valueOffX = 220
local list = ListField:new{posX = 64, posY = 96}
list:addField(SelfTestField:new{label = "MAIN I/O", VALUE_OFFSETX = valueOffX, status = SelfTestStatusEnum.OK})
list:addField(SelfTestField:new{label = "SKIN CONFIG", VALUE_OFFSETX = valueOffX, status = SelfTestStatusEnum.OK, callback = checkSkinConfig})
list:addField(SelfTestField:new{label = "IR NETWORK", VALUE_OFFSETX = valueOffX, status = SelfTestStatusEnum.ERROR, callback = checkNetwork})
this:addField(list)
return self
return this
end
---@param deltaTime number # frametime in seconds
@ -47,4 +65,12 @@ function BootPage:drawBackground(deltaTime)
gfx.Fill()
end
function BootPage:render(deltaTime)
Page.render(self, deltaTime)
local skinconfig = self.content[3].content[2] --this is hacktastic
local network = self.content[3].content[3] --this is also hacktastic
end
return BootPage

View File

@ -1,5 +1,4 @@
require("common.class")
local ServiceField = require("titlescreen.fields.service.servicefield")
local ServicePage = require("titlescreen.pages.service.servicepage")
local ListField = require("titlescreen.fields.service.listfield")
local ColorGradientField = require("titlescreen.fields.service.colorgradientfield")
@ -8,7 +7,9 @@ local ColorGradientField = require("titlescreen.fields.service.colorgradientfiel
local ColorCheckPage = {
__tostring = function() return "ColorCheckPage" end,
GRADIENT_SPACING = ServiceField.FONT_SIZE + 8,
PADDING = {56, 120, 0, 56}, --{left, top, right, bottom}
GRADIENT_SPACING = 32,
SEPARATOR_LINE_COLOR = {255, 255, 255, 255},
SEPARATOR_LINE_WIDTH = 4,
@ -29,25 +30,23 @@ function ColorCheckPage:new(o)
"BACK BUTTON = EXIT"
}
return Inherit(self, o, ServicePage)
end
---Initialize members
---@return ColorCheckPage
function ColorCheckPage:init()
ServicePage.init(self)
local this = Inherit(self, o, ServicePage)
local height = self.GRADIENT_SPACING
local list = ListField:new()
list:addField(ColorGradientField:new{label = "RED", value = {255, 0, 0, 255}, aabbH = height})
list:addField(ColorGradientField:new{label = "YELLOW", value = {255, 255, 0, 255}, aabbH = height})
list:addField(ColorGradientField:new{label = "GREEN", value = {0, 255, 0, 255}, aabbH = height})
list:addField(ColorGradientField:new{label = "CYAN", value = {0, 255, 255, 255}, aabbH = height})
list:addField(ColorGradientField:new{label = "BLUE", value = {0, 0, 255, 255}, aabbH = height})
list:addField(ColorGradientField:new{label = "MAGENTA", value = {255, 0, 255, 255}, aabbH = height})
list:addField(ColorGradientField:new{label = "WHITE", value = {255, 255, 255, 255}, aabbH = height})
list:refreshFields()
self:addField(ColorGradientField:new{label = "RED", value = {255, 0, 0, 255}, aabbH = height})
self:addField(ColorGradientField:new{label = "YELLOW", value = {255, 255, 0, 255}, aabbH = height})
self:addField(ColorGradientField:new{label = "GREEN", value = {0, 255, 0, 255}, aabbH = height})
self:addField(ColorGradientField:new{label = "CYAN", value = {0, 255, 255, 255}, aabbH = height})
self:addField(ColorGradientField:new{label = "BLUE", value = {0, 0, 255, 255}, aabbH = height})
self:addField(ColorGradientField:new{label = "MAGENTA", value = {255, 0, 255, 255}, aabbH = height})
self:addField(ColorGradientField:new{label = "WHITE", value = {255, 255, 255, 255}, aabbH = height})
this:addField(list)
this:refreshFields()
return self
return this
end
---@param button integer # options are under the `game` table prefixed with `BUTTON`
@ -73,7 +72,7 @@ function ColorCheckPage:_drawArrows(deltaTime)
local stepW = ColorGradientField.GRADIENT_WIDTH / ColorGradientField.GRADIENT_STEPS
gfx.BeginPath()
for i = 0, 3 do
local posX = i * stepW
local posX = ColorGradientField.GRADIENT_X_OFFSET + i * stepW
gfx.MoveTo(posX + self.SEPARATOR_ARROW_MARGIN, self.SEPARATOR_ARROW_SIZE - self.SEPARATOR_ARROW_MARGIN)
gfx.LineTo(posX + stepW / 2, self.SEPARATOR_ARROW_MARGIN)
gfx.LineTo(posX + stepW - self.SEPARATOR_ARROW_MARGIN, self.SEPARATOR_ARROW_SIZE - self.SEPARATOR_ARROW_MARGIN)
@ -97,14 +96,17 @@ end
---@param deltaTime number # frametime in seconds
function ColorCheckPage:drawBackground(deltaTime)
ServicePage.drawBackground(self, deltaTime)
gfx.Save()
gfx.Translate(self.PADDING[1], self.PADDING[2])
gfx.Translate(ServicePage.PADDING[1], self.PADDING[2] + #self.content * self.GRADIENT_SPACING)
local list = self.content[1]
local posX = list.posX
local posY = list.posY + list.aabbH
gfx.Translate(posX, posY)
self:_drawSeparator(deltaTime)
gfx.Translate(0, self.SEPARATOR_LINE_WIDTH)
self:_drawArrows(deltaTime)
gfx.Translate(0, self.SEPARATOR_ARROW_SIZE)
self:_drawArrowText(deltaTime)

View File

@ -2,6 +2,7 @@ require("common.class")
local ServicePage = require("titlescreen.pages.service.servicepage")
local InputButtonField = require("titlescreen.fields.service.inputbuttonfield")
local InputKnobField = require("titlescreen.fields.service.inputknobfield")
local ListField = require("titlescreen.fields.service.listfield")
---@class InputCheckPage: ServicePage
local InputCheckPage = {
@ -17,23 +18,24 @@ function InputCheckPage:new(o)
o.title = o.title or "INPUT CHECK"
o.footer = o.footer or "BACK BUTTON = EXIT"
return Inherit(self, o, ServicePage)
end
local this = Inherit(self, o, ServicePage)
---Initialize members
---@return InputCheckPage
function InputCheckPage:init()
ServicePage.init(self)
self:addField(InputButtonField:new{label="START BUTTON", button=game.BUTTON_STA})
self:addField(InputButtonField:new{label="A BUTTON", button=game.BUTTON_BTA})
self:addField(InputButtonField:new{label="B BUTTON", button=game.BUTTON_BTB})
self:addField(InputButtonField:new{label="C BUTTON", button=game.BUTTON_BTC})
self:addField(InputButtonField:new{label="D BUTTON", button=game.BUTTON_BTD})
self:addField(InputButtonField:new{label="FX L BUTTON", button=game.BUTTON_FXL})
self:addField(InputButtonField:new{label="FX R BUTTON", button=game.BUTTON_FXR})
self:addField(InputKnobField:new{label="ANALOG VOLUME L", knob=0})
self:addField(InputKnobField:new{label="ANALOG VOLUME R", knob=1})
return self
local list = ListField:new()
list:addField(InputButtonField:new{label="START BUTTON", button=game.BUTTON_STA})
list:addField(InputButtonField:new{label="A BUTTON", button=game.BUTTON_BTA})
list:addField(InputButtonField:new{label="B BUTTON", button=game.BUTTON_BTB})
list:addField(InputButtonField:new{label="C BUTTON", button=game.BUTTON_BTC})
list:addField(InputButtonField:new{label="D BUTTON", button=game.BUTTON_BTD})
list:addField(InputButtonField:new{label="FX L BUTTON", button=game.BUTTON_FXL})
list:addField(InputButtonField:new{label="FX R BUTTON", button=game.BUTTON_FXR})
list:addField(InputKnobField:new{label="ANALOG VOLUME L", knob=0})
list:addField(InputKnobField:new{label="ANALOG VOLUME R", knob=1})
list:refreshFields()
this:addField(list)
this:refreshFields()
return this
end
---@param button integer # options are under the `game` table prefixed with `BUTTON`

View File

@ -20,44 +20,19 @@ function MainMenuPage:new(o)
o.title = o.title or "MAIN MENU"
return Inherit(self, o, ServicePage)
end
local this = Inherit(self, o, ServicePage)
---Initialize members
---@return MainMenuPage
function MainMenuPage:init()
ServicePage.init(self)
local list = ListField:new()
list:addField(ServiceLinkField:new{label = "INPUT CHECK", link = InputCheckPage:new()})
list:addField(ServiceLinkField:new{label = "SCREEN CHECK", link = ScreenCheckPage:new()})
list:addField(ServiceLinkField:new{label = "COLOR CHECK", link = ColorCheckPage:new()})
list:addField(ServiceLinkField:new{label = "VERSION INFORMATION", link = VersionInfoPage:new()})
list:refreshFields()
local posX = self.PADDING[1]
local posY = self.PADDING[2]
this:addField(list)
this:refreshFields()
local list = ListField:new{
posX = posX,
posY = posY,
}:init()
list:addField(ServiceLinkField:new{
label = "INPUT CHECK",
link = InputCheckPage:new():init(),
})
list:addField(ServiceLinkField:new{
label = "SCREEN CHECK",
link = ScreenCheckPage:new():init(),
})
list:addField(ServiceLinkField:new{
label = "COLOR CHECK",
link = ColorCheckPage:new():init(),
})
list:addField(ServiceLinkField:new{
label = "VERSION INFORMATION",
link = VersionInfoPage:new():init(),
})
self:addField(list)
self:refreshFields()
return self
return this
end
return MainMenuPage

View File

@ -9,12 +9,19 @@ local ServiceField = require("titlescreen.fields.service.servicefield")
---@field selectedIndex integer
---@field footer string[]
---@field content ServiceField[]
---@field FONT_SIZE number
---@field FONT_FACE string
---@field FONT_COLOR integer[] # {r, g, b, a}
---@field PADDING number[] # {left, top, right, bottom}
---@field PAGE_PADDING number[] # {left, top, right, bottom}
---@field FOOTER string|string[]
---@field FOOTER_SPACING number
local ServicePage = {
__tostring = function() return "ServicePage" end,
FONT_SIZE = ServiceField.FONT_SIZE,
FONT_FACE = ServiceField.FONT_FACE,
FONT_COLOR = ServiceField.FONT_COLOR, --{r, g, b, a}
PADDING = {92, 128, 0, 56}, --{left, top, right, bottom}
PADDING = {100, 128, 0, 56}, --{left, top, right, bottom}
PAGE_PADDING = {16, 16, 16, 16}, --{left, top, right, bottom}
FOOTER = {
"A/B BUTTON = SELECT ITEM",
@ -51,69 +58,16 @@ function ServicePage:refreshFields()
Page.refreshFields(self)
end
---@param deltaTime number # frametime in seconds
function ServicePage:drawBackground(deltaTime)
gfx.BeginPath()
gfx.FillColor(0, 0, 0)
gfx.Rect(0, 0, Dim.design.width, Dim.design.height)
gfx.Fill()
end
---@param deltaTime number # frametime in seconds
function ServicePage:drawHeader(deltaTime)
local pageTitleTopPadding = self.PAGE_PADDING[2]
local lineHeight = self.FOOTER_SPACING
gfx.BeginPath()
gfx.FontSize(self.FONT_SIZE)
gfx.LoadSkinFont(self.FONT_FACE)
gfx.FillColor(table.unpack(self.FONT_COLOR))
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER | gfx.TEXT_ALIGN_TOP)
if type(self.title) == "table" then
for index, line in ipairs(self.title) do
gfx.Text(line, Dim.design.width / 2, pageTitleTopPadding + (index-1) * lineHeight)
end
elseif type(self.title) == "string" then
gfx.Text(self.title, Dim.design.width / 2, pageTitleTopPadding)
end
end
---@param deltaTime number # frametime in seconds
function ServicePage:drawFooter(deltaTime)
local footer = self.content[self.selectedIndex] and self.content[self.selectedIndex].footer or self.footer
local pageFooterBottomPadding = self.PADDING[4]
local lineHeight = self.FOOTER_SPACING
gfx.BeginPath()
gfx.FontSize(self.FONT_SIZE)
gfx.LoadSkinFont(self.FONT_FACE)
gfx.FillColor(table.unpack(self.FONT_COLOR))
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER | gfx.TEXT_ALIGN_BOTTOM)
local yFooterBase
if type(footer) == "table" then
yFooterBase = Dim.design.height - pageFooterBottomPadding - #footer * lineHeight
for index, line in ipairs(footer) do
gfx.Text(line, Dim.design.width / 2, yFooterBase + (index-1) * lineHeight)
end
elseif type(footer) == "string" then
yFooterBase = Dim.design.height - pageFooterBottomPadding
gfx.Text(footer, Dim.design.width / 2, yFooterBase)
end
end
---@param button integer # options are under the `game` table prefixed with `BUTTON`
function ServicePage:handleButtonInput(button)
local field = self.content[self.selectedIndex]
if field and field.handleButtonInput then
-- if the field indicates that the button input has been handled in a
-- way that requires no further processing, return from this function
if field:handleButtonInput(button) then
return
end
end
-- default behaviour
field:deactivate()
-- default behaviour:
local direction = 0
@ -122,18 +76,20 @@ function ServicePage:handleButtonInput(button)
self.viewHandler:back()
end
return
end
if button == game.BUTTON_BTA then
elseif button == game.BUTTON_BTA then
direction = -1
elseif button == game.BUTTON_BTB then
direction = 1
end
if direction ~= 0 then
field:deactivate()
self.selectedIndex = Util.modIndex(self.selectedIndex + direction, #self.content)
field = self.content[self.selectedIndex]
field:focus({direction = direction}) -- send direction as the message
end
end
---@param knob integer # `0` = Left, `1` = Right
@ -144,4 +100,73 @@ function ServicePage:handleKnobInput(knob, delta)
end
end
---@param deltaTime number # frametime in seconds
function ServicePage:drawBackground(deltaTime)
gfx.BeginPath()
gfx.FillColor(0, 0, 0)
gfx.Rect(0, 0, Dim.design.width, Dim.design.height)
gfx.Fill()
end
---@param deltaTime number # frametime in seconds
function ServicePage:drawContent(deltaTime)
gfx.Save()
gfx.Translate(self.PADDING[1], self.PADDING[2])
local contentW = Dim.design.width - self.PADDING[1] - self.PADDING[3]
local contentH = Dim.design.height - self.PADDING[2] - self.PADDING[4]
gfx.Scissor(0, 0, contentW, contentH)
Page.drawContent(self, deltaTime)
gfx.Restore()
end
---@param deltaTime number # frametime in seconds
function ServicePage:drawHeader(deltaTime)
local lineHeight = self.FOOTER_SPACING
gfx.BeginPath()
gfx.FontSize(self.FONT_SIZE)
gfx.LoadSkinFont(self.FONT_FACE)
gfx.FillColor(table.unpack(self.FONT_COLOR))
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER | gfx.TEXT_ALIGN_TOP)
if type(self.title) == "table" then
for index, line in ipairs(self.title) do
gfx.Text(line, Dim.design.width / 2, (index-1) * lineHeight)
end
elseif type(self.title) == "string" then
gfx.Text(self.title, Dim.design.width / 2, 0)
end
end
---@param deltaTime number # frametime in seconds
function ServicePage:drawFooter(deltaTime)
local footer = self.content[self.selectedIndex] and self.content[self.selectedIndex].footer or self.footer
local lineHeight = self.FOOTER_SPACING
gfx.BeginPath()
gfx.FontSize(self.FONT_SIZE)
gfx.LoadSkinFont(self.FONT_FACE)
gfx.FillColor(table.unpack(self.FONT_COLOR))
gfx.TextAlign(gfx.TEXT_ALIGN_CENTER | gfx.TEXT_ALIGN_BOTTOM)
if type(footer) == "table" then
local yFooterBase = -#footer * lineHeight
for index, line in ipairs(footer) do
gfx.Text(line, Dim.design.width / 2, yFooterBase + (index-1) * lineHeight)
end
elseif type(footer) == "string" then
gfx.Text(footer, Dim.design.width / 2, 0)
end
end
---@param deltaTime number # frametime in seconds
function ServicePage:drawForeground(deltaTime)
gfx.Save()
gfx.Translate(0, self.PAGE_PADDING[2])
self:drawHeader(deltaTime)
gfx.Restore()
gfx.Save()
gfx.Translate(0, Dim.design.height - self.PAGE_PADDING[4])
self:drawFooter(deltaTime)
gfx.Restore()
end
return ServicePage

View File

@ -5,7 +5,7 @@ local Version = require("common.version")
local ServicePage = require("titlescreen.pages.service.servicepage")
local ServiceField = require("titlescreen.fields.service.servicefield")
local UpdateField = require("titlescreen.fields.service.updatefield")
local SpacerField = require("titlescreen.fields.service.spacerfield")
local ListField = require("titlescreen.fields.service.listfield")
local function getGameLogValue(prefix, str)
local pattern = prefix .. ":%s*([^\r\n]*)"
@ -28,36 +28,27 @@ function VersionInfoPage:new(o)
"START BUTTON = UPDATE",
"BACK BUTTON = EXIT"
}
o.selectedIndex = o.selectedIndex or 3
o.selectedIndex = o.selectedIndex or 1
return Inherit(self, o, ServicePage)
end
---Initialize members
---@return VersionInfoPage
function VersionInfoPage:init()
ServicePage.init(self)
local this = Inherit(self, o, ServicePage)
local logStr = ReadGameFile("log_usc-game.exe.txt")
self:addField(ServiceField:new{label = "SKIN ID CODE", value = Version.getLongVersion()})
local list = ListField:new{selectedIndex = 2, locked = true}
list:addField(ServiceField:new{label = "SKIN ID CODE", value = Version.getLongVersion(), MARGIN = {0, 0, 0, 24}})
list:addField(UpdateField:new{label = "USC VERSION", value = getGameLogValue("Version", logStr)})
list:addField(ServiceField:new{label = "USC BRANCH", value = GameConfig["UpdateChannel"]})
list:addField(ServiceField:new{label = "USC GIT COMMIT", value = getGameLogValue("Git commit", logStr), MARGIN = {0, 0, 0, 24}})
list:addField(ServiceField:new{label = "GL VERSION", value = getGameLogValue("OpenGL Version", logStr)})
list:addField(ServiceField:new{label = "GLSL VERSION", value = getGameLogValue("OpenGL Shading Language Version", logStr)})
list:addField(ServiceField:new{label = "GL RENDERER", value = getGameLogValue("OpenGL Renderer", logStr)})
list:addField(ServiceField:new{label = "GL VENDOR", value = getGameLogValue("OpenGL Vendor", logStr)})
list:refreshFields()
self:addField(SpacerField:new())
this:addField(list)
this:refreshFields()
self:addField(UpdateField:new{label = "USC VERSION", value = getGameLogValue("Version", logStr)})
self:addField(ServiceField:new{label = "USC BRANCH", value = GameConfig["UpdateChannel"]})
self:addField(ServiceField:new{label = "USC GIT COMMIT", value = getGameLogValue("Git commit", logStr)})
self:addField(SpacerField:new())
self:addField(ServiceField:new{label = "GL VERSION", value = getGameLogValue("OpenGL Version", logStr)})
self:addField(ServiceField:new{label = "GLSL VERSION", value = getGameLogValue("OpenGL Shading Language Version", logStr)})
self:addField(ServiceField:new{label = "GL RENDERER", value = getGameLogValue("OpenGL Renderer", logStr)})
self:addField(ServiceField:new{label = "GL VENDOR", value = getGameLogValue("OpenGL Vendor", logStr)})
self.content[self.selectedIndex]:focus()
return self
return this
end
---@param button integer # options are under the `game` table prefixed with `BUTTON`

View File

@ -30,7 +30,7 @@ local rootMenu = {
]]
local currentpage = MainMenuPage:new():init()
local currentpage = MainMenuPage:new()
local pageview = PageView:new(currentpage)