diff --git a/CardLayout.lua b/CardLayout.lua new file mode 100644 index 0000000..2bcefc2 --- /dev/null +++ b/CardLayout.lua @@ -0,0 +1,426 @@ +--- Imports +local _ = require("util.score") +local sound = require("util.sound") +local eventHook = require("util.eventHook") +local renderHelpers = require("util.renderHelpers") + +local Display = require("modules.display") + +local Solyd = require("modules.solyd") +local hooks = require("modules.hooks") +local useCanvas = hooks.useCanvas + +local Button = require("components.Button") +local SmolButton = require("components.SmolButton") +local BasicButton = require("components.BasicButton") +local BigText = require("components.BigText") +local bigFont = require("fonts.bigfont") +local SmolText = require("components.SmolText") +local smolFont = require("fonts.smolfont") +local BasicText = require("components.BasicText") +local Rect = require("components.Rect") +local RenderCanvas = require("components.RenderCanvas") +local Core = require("core.ShopState") +local Pricing = require("core.Pricing") +local ShopRunner = require("core.ShopRunner") +local ConfigValidator = require("core.ConfigValidator") + +local loadRIF = require("modules.rif") + +local function render(canvas, display, props, theme, version) + local layoutName = theme.formatting.layoutFile + local elements = {} + + local categories = renderHelpers.getCategories(props.shopState.products) + local selectedCategory = props.shopState.selectedCategory + local shopProducts = renderHelpers.getDisplayedProducts(categories[selectedCategory].products, props.config.settings) + local currency = props.shopState.selectedCurrency + + local headerSuffix = "" + if currency.name and currency.krypton and currency.krypton.currency then + headerSuffix = "." .. currency.krypton.currency.name_suffix + end + local headerPadding = 2*6 + local headerWidth + local headerText = currency.host + if currency.name then + headerText = currency.name + headerWidth = bigFont:getWidth(currency.name) + else + headerWidth = bigFont:getWidth(currency.host) + end + local headerStartX = 1 + local headerAlign = renderHelpers.getThemeSetting(theme, "formatting.headerAlign", layoutName) + if headerAlign == "center" then + headerStartX = math.floor((display.bgCanvas.width - headerWidth) / 2) + elseif headerAlign == "right" then + headerStartX = display.bgCanvas.width - headerWidth - headerPadding - (#headerSuffix * 2) + end + local header = BigText { + display = display, + text = headerText, + x = headerStartX, + y = 3*2, + align = "left", + bg = renderHelpers.getThemeSetting(theme, "colors.bgColor", layoutName), + color = renderHelpers.getThemeSetting(theme, "colors.headerColor", layoutName) + } + table.insert(elements, header) + + if headerSuffix and #headerSuffix > 0 then + local suffix = BasicText { + display = display, + text = headerSuffix, + x = (headerStartX + headerWidth + headerPadding)/2, + y = 6, + align = "left", + bg = renderHelpers.getThemeSetting(theme, "colors.bgColor", layoutName), + color = renderHelpers.getThemeSetting(theme, "colors.headerSuffixColor", layoutName) + } + table.insert(elements, suffix) + end + + local subHeaderWidth = math.max( (display.bgCanvas.width / 2) / 2, #props.config.branding.title) + local subheaderStartX = 1 + if headerAlign == "center" then + subheaderStartX = math.floor(((display.bgCanvas.width / 2) - subHeaderWidth) / 2) + elseif headerAlign == "right" then + subheaderStartX = (display.bgCanvas.width/2) - subHeaderWidth + end + local subHeader = BasicText { + display = display, + text = props.config.branding.title, + x = subheaderStartX, + y = 6 + 2, + align = headerAlign, + bg = renderHelpers.getThemeSetting(theme, "colors.subheaderBgColor", layoutName), + color = renderHelpers.getThemeSetting(theme, "colors.subheaderColor", layoutName), + width = subHeaderWidth + } + table.insert(elements, Rect { + display = display, + x = (subheaderStartX*2) - 1, + y = ((6+2)*3) - 3, + width = (subHeaderWidth*2) + 1, + height = 4, + color = renderHelpers.getThemeSetting(theme, "colors.subheaderBgColor", layoutName) + }) + table.insert(elements, Rect { + display = display, + x = (subheaderStartX*2) - 2, + y = ((6+2)*3) - 2, + width = 1, + height = 2, + color = renderHelpers.getThemeSetting(theme, "colors.subheaderBgColor", layoutName) + }) + table.insert(elements, Rect { + display = display, + x = ((subheaderStartX+subHeaderWidth)*2), + y = ((6+2)*3) - 2, + width = 1, + height = 2, + color = renderHelpers.getThemeSetting(theme, "colors.subheaderBgColor", layoutName) + }) + table.insert(elements, subHeader) + table.insert(elements, Rect { + display = display, + x = 5, + y = 9*3, + width = display.bgCanvas.width-10, + height = 1, + color = renderHelpers.getThemeSetting(theme, "colors.dividerColor", layoutName) + }) + + if renderHelpers.getThemeSetting(theme, "settings.showCredits", layoutName) then + local credits = BasicText { + display = display, + text = "Radon", + x = 1, + y = 1, + align = "left", + bg = renderHelpers.getThemeSetting(theme, "colors.bgColor", layoutName), + color = renderHelpers.getThemeSetting(theme, "colors.creditsColor", layoutName) + } + table.insert(elements, credits) + end + + local cardX = (2*2) + 1 + local cardY = (10*3)+1 + local helperX = 1 + local currencySymbol = renderHelpers.getCurrencySymbol(currency, "small") + local productBgColors = renderHelpers.getThemeSetting(theme, "colors.productBgColors", layoutName) + local productBgShadowColors = renderHelpers.getThemeSetting(theme, "colors.productBgShadowColors", layoutName) + + for i = 1, #shopProducts do + local product = shopProducts[i] + local productAddr = product.address .. "@" + if not props.shopState.selectedCurrency.name then + productAddr = product.address + end + product.quantity = product.quantity or 0 + local qtyColor = renderHelpers.getThemeSetting(theme, "colors.normalQtyColor", layoutName) + if product.quantity == 0 then + qtyColor = renderHelpers.getThemeSetting(theme, "colors.outOfStockQtyColor", layoutName) + elseif product.quantity < 10 then + qtyColor = renderHelpers.getThemeSetting(theme, "colors.lowQtyColor", layoutName) + elseif product.quantity < 64 then + qtyColor = renderHelpers.getThemeSetting(theme, "colors.warningQtyColor", layoutName) + end + local productPrice = Pricing.getProductPrice(product, props.shopState.selectedCurrency) + local priceString = tostring(productPrice) .. currencySymbol + local productBgColor = productBgColors[((i-1) % #productBgColors) + 1] + local productBgShadowColor = productBgShadowColors[((i-1) % #productBgShadowColors) + 1] + local cardWidth = 2 + 2 + (math.max( + #product.name, + #(" Left: " .. tostring(product.quantity)), + #(" Name: " .. productAddr), + 2+#priceString + )*2) + if cardWidth % 2 ~= 0 then + cardWidth = cardWidth + 1 + end + if cardX + cardWidth > display.bgCanvas.width then + cardX = (2*2) + 1 + cardY = cardY + 8*3 + end + + -- Inner card + table.insert(elements, Rect { + display = display, + x = cardX + 1, + y = cardY + 4, + width = cardWidth - 2, + height = (6*3) - 2, + color = productBgColor + }) + -- Left border + table.insert(elements, Rect { + display = display, + x = cardX, + y = cardY + 5, + width = 1, + height = (5*3) - 1, + color = productBgColor + }) + -- Right Border + table.insert(elements, Rect { + display = display, + x = cardX + cardWidth - 1, + y = cardY + 5, + width = 1, + height = (5*3) - 1, + color = productBgColor + }) + -- Right shadow + table.insert(elements, Rect { + display = display, + x = cardX + cardWidth, + y = cardY + 6, + width = 1, + height = (5*3), + color = productBgShadowColor + }) + -- Bottom shadow + table.insert(elements, Rect { + display = display, + x = cardX + 2, + y = cardY + (6*3) + 2, + width = cardWidth - 2, + height = 1, + color = productBgShadowColor + }) + -- Bottom right shadow + table.insert(elements, Rect { + display = display, + x = cardX + cardWidth - 1, + y = cardY + (6*3) + 1, + width = 1, + height = 1, + color = productBgShadowColor + }) + -- Product name + table.insert(elements, BasicText { + display = display, + text = product.name, + x = math.floor((cardX/2) + 2), + y = math.floor((cardY/3) + 3), + align = "left", + bg = productBgColor, + color = renderHelpers.getThemeSetting(theme, "colors.productNameColor", layoutName) + }) + -- Product quantity + table.insert(elements, BasicText { + display = display, + text = " Left: ", + x = math.floor((cardX/2) + 2), + y = math.floor((cardY/3) + 4), + align = "left", + bg = productBgColor, + color = renderHelpers.getThemeSetting(theme, "colors.fieldLabelColor", layoutName) + }) + table.insert(elements, BasicText { + display = display, + text = tostring(product.quantity), + x = math.floor((cardX/2) + 2) + #(" Left: "), + y = math.floor((cardY/3) + 4), + align = "left", + bg = productBgColor, + color = qtyColor + }) + -- Product address (metaname) + table.insert(elements, BasicText { + display = display, + text = " Name: ", + x = math.floor((cardX/2) + 2), + y = math.floor((cardY/3) + 5), + align = "left", + bg = productBgColor, + color = renderHelpers.getThemeSetting(theme, "colors.fieldLabelColor", layoutName) + }) + table.insert(elements, BasicText { + display = display, + text = productAddr, + x = math.floor((cardX/2) + 2) + #(" Name: "), + y = math.floor((cardY/3) + 5), + align = "left", + bg = productBgColor, + color = renderHelpers.getThemeSetting(theme, "colors.addressColor", layoutName) + }) + -- Product price + table.insert(elements, BasicText { + display = display, + text = " " .. priceString .. " ", + x = math.floor(((cardX + cardWidth)/2) - (#priceString + 2)), + y = math.floor((cardY/3) + 6), + align = "center", + bg = renderHelpers.getThemeSetting(theme, "colors.priceBgColor", layoutName), + color = renderHelpers.getThemeSetting(theme, "colors.priceColor", layoutName), + }) + -- Product kristpay helper + if props.shopState.selectedCurrency.name then + local helperString = productAddr .. props.shopState.selectedCurrency.name + table.insert(elements, BasicText { + display = display, + text = helperString, + x = helperX, + y = math.floor(cardY/3), + align = "left", + bg = renderHelpers.getThemeSetting(theme, "colors.bgColor", layoutName), + color = renderHelpers.getThemeSetting(theme, "colors.bgColor", layoutName), + }) + helperX = helperX + #helperString + 1 + end + + cardX = cardX + cardWidth + 4 + end + + -- Currencies + if #props.config.currencies > 1 then + local currencyBgColors = renderHelpers.getThemeSetting(theme, "colors.currencyBgColors", layoutName) + local maxCurrencyLeftX = math.floor( ((subheaderStartX*2) - 3) / 2) + local minCurrencyRightX = math.ceil( (((subheaderStartX+subHeaderWidth)*2) + 3) / 2) + local currencyX = 2 + for i = 1, #props.config.currencies do + local displayCurrency = props.config.currencies[i] + local displaySymbol = " " .. renderHelpers.getCurrencySymbol(displayCurrency, "small") .. " " + local currencyBgColor = currencyBgColors[((i-1) % #currencyBgColors) + 1] + if currencyX + #displaySymbol > maxCurrencyLeftX then + currencyX = minCurrencyRightX + end + table.insert(elements, BasicButton { + display = display, + text = displaySymbol, + x = currencyX, + y = 6 + 2, + align = "left", + bg = currencyBgColor, + color = renderHelpers.getThemeSetting(theme, "colors.currencyTextColor", layoutName), + onClick = function() + props.shopState.selectedCurrency = props.config.currencies[i] + props.shopState.lastTouched = os.epoch("utc") + if props.config.settings.playSounds then + sound.playSound(props.speaker, props.config.sounds.button) + end + end + }) + currencyX = currencyX + #displaySymbol + 2 + end + end + + -- Categories + if #categories > 1 then + local categoryBgColors = renderHelpers.getThemeSetting(theme, "colors.categoryBgColors", layoutName) + local categoriesWidth = 0 + for i = 1, #categories do + local category = categories[i] + local categoryName = category.name + if i == selectedCategory then + categoryName = "[" .. categoryName .. "]" + end + categoriesWidth = categoriesWidth + #categoryName + 4 + end + local categoryX = math.floor((display.bgCanvas.width / 4) - (categoriesWidth / 2)) + for i = 1, #categories do + local category = categories[i] + local categoryName = category.name + local categoryBgColor = categoryBgColors[((i-1) % #categoryBgColors) + 1] + if i == selectedCategory then + categoryName = "[" .. categoryName .. "]" + categoryBgColor = renderHelpers.getThemeSetting(theme, "colors.activeCategoryColor", layoutName) + end + table.insert(elements, BasicButton { + display = display, + text = " " .. categoryName .. " ", + x = categoryX, + y = math.floor(display.bgCanvas.height / 3) - 1, + align = "left", + bg = categoryBgColor, + color = renderHelpers.getThemeSetting(theme, "colors.categoryTextColor", layoutName), + onClick = function() + props.shopState.selectedCategory = i + props.shopState.lastTouched = os.epoch("utc") + if props.config.settings.playSounds then + sound.playSound(props.speaker, props.config.sounds.button) + end + end + }) + categoryX = categoryX + #categoryName + 4 + end + end + + if props.config.settings.showFooter then + local footerMessage + if props.shopState.selectedCurrency.name or not props.config.lang.footerNoName then + footerMessage = props.config.lang.footer + else + footerMessage = props.config.lang.footerNoName + end + if props.shopState.selectedCurrency.name and footerMessage:find("%%name%%") then + footerMessage = footerMessage:gsub("%%name%%", props.shopState.selectedCurrency.name) + end + if footerMessage:find("%%addr%%") then + footerMessage = footerMessage:gsub("%%addr%%", props.shopState.selectedCurrency.host) + end + if footerMessage:find("%%version%%") then + footerMessage = footerMessage:gsub("%%version%%", version) + end + + if props.shopState.selectedCurrency then + local footer = BasicText { + display = display, + text = footerMessage, + x = 1, + y = math.floor(display.bgCanvas.height / 3) , + align = renderHelpers.getThemeSetting(theme, "formatting.footerAlign", layoutName), + bg = renderHelpers.getThemeSetting(theme, "colors.footerBgColor", layoutName), + color = renderHelpers.getThemeSetting(theme, "colors.footerColor", layoutName), + width = math.floor(display.bgCanvas.width / 2) + } + table.insert(elements, footer) + end + end + + return elements +end + +return render \ No newline at end of file diff --git a/DefaultLayout.lua b/DefaultLayout.lua new file mode 100644 index 0000000..c00a2dc --- /dev/null +++ b/DefaultLayout.lua @@ -0,0 +1,442 @@ +--- Imports +local _ = require("util.score") +local sound = require("util.sound") +local eventHook = require("util.eventHook") +local renderHelpers = require("util.renderHelpers") + +local Display = require("modules.display") + +local Solyd = require("modules.solyd") +local hooks = require("modules.hooks") +local useCanvas = hooks.useCanvas + +local Button = require("components.Button") +local SmolButton = require("components.SmolButton") +local BigText = require("components.BigText") +local bigFont = require("fonts.bigfont") +local SmolText = require("components.SmolText") +local smolFont = require("fonts.smolfont") +local BasicText = require("components.BasicText") +local Rect = require("components.Rect") +local RenderCanvas = require("components.RenderCanvas") +local Core = require("core.ShopState") +local Pricing = require("core.Pricing") +local ShopRunner = require("core.ShopRunner") +local ConfigValidator = require("core.ConfigValidator") + + +local loadRIF = require("modules.rif") + +local function render(canvas, display, props, theme, version) + local elements = {} + + local categories = renderHelpers.getCategories(props.shopState.products) + local selectedCategory = props.shopState.selectedCategory + + local currencyEndX = 3 + if #props.config.currencies > 1 then + for i = 1, #props.config.currencies do + local symbol = renderHelpers.getCurrencySymbol(props.config.currencies[i], "large") + local symbolSize = bigFont:getWidth(symbol)+6 + currencyEndX = currencyEndX + symbolSize + 2 + end + end + + local categoryX = display.bgCanvas.width - 2 + if #categories > 1 then + for i = #categories, 1, -1 do + local category = categories[i] + local categoryName = category.name + if i == selectedCategory then + categoryName = "[" .. categoryName .. "]" + end + local categoryWidth = smolFont:getWidth(categoryName)+6 + categoryX = categoryX - categoryWidth - 2 + end + end + + local headerCx = math.floor((display.bgCanvas.width - bigFont:getWidth(props.config.branding.title)) / 2) + local header + -- TODO: Change header font size based on width + if theme.formatting.headerAlign == "center" and headerCx < currencyEndX and #categories == 1 then + table.insert(elements, Rect { display=display, x=1, y=1, width=currencyEndX, height=bigFont.height+6, color=theme.colors.headerBgColor }) + header = BigText { display=display, text=props.config.branding.title, x=currencyEndX, y=1, align="left", bg=theme.colors.headerBgColor, color = theme.colors.headerColor, width=display.bgCanvas.width } + elseif theme.formatting.headerAlign == "center" and headerCx+bigFont:getWidth(props.config.branding.title) > categoryX and #categories > 1 then + table.insert(elements, Rect { display=display, x=categoryX, y=1, width=display.bgCanvas.width-categoryX+1, height=bigFont.height+6, color=theme.colors.headerBgColor }) + header = BigText { display=display, text=props.config.branding.title, x=1, y=1, align="right", bg=theme.colors.headerBgColor, color = theme.colors.headerColor, width=categoryX-1 } + else + header = BigText { display=display, text=props.config.branding.title, x=1, y=1, align=theme.formatting.headerAlign, bg=theme.colors.headerBgColor, color = theme.colors.headerColor, width=display.bgCanvas.width } + end + + table.insert(elements, header) + + local footerHeight = 0 + if props.config.settings.showFooter then + local footerMessage + if props.shopState.selectedCurrency.name or not props.config.lang.footerNoName then + footerMessage = props.config.lang.footer + else + footerMessage = props.config.lang.footerNoName + end + if props.shopState.selectedCurrency.name and footerMessage:find("%%name%%") then + footerMessage = footerMessage:gsub("%%name%%", props.shopState.selectedCurrency.name) + end + if footerMessage:find("%%addr%%") then + footerMessage = footerMessage:gsub("%%addr%%", props.shopState.selectedCurrency.host) + end + if footerMessage:find("%%version%%") then + footerMessage = footerMessage:gsub("%%version%%", version) + end + + if props.shopState.selectedCurrency then + local footer + if smolFont:getWidth(footerMessage) < display.bgCanvas.width then + footer = SmolText { display=display, text=footerMessage, x=1, y=display.bgCanvas.height-smolFont.height-4, align=theme.formatting.footerAlign, bg=theme.colors.footerBgColor, color = theme.colors.footerColor, width=display.bgCanvas.width } + else + footer = BasicText { display=display, text=footerMessage, x=1, y=math.floor(display.bgCanvas.height/3), align=theme.formatting.footerAlign, bg=theme.colors.footerBgColor, color = theme.colors.footerColor, width=math.ceil(display.bgCanvas.width/2) } + end + + table.insert(elements, footer) + end + footerHeight = smolFont.height + 4 + end + + local maxAddrWidth = 0 + local maxQtyWidth = 0 + local maxPriceWidth = 0 + local maxNameWidth = 0 + props.shopState.numCategories = #categories + local catName = categories[selectedCategory].name + local shopProducts = renderHelpers.getDisplayedProducts(categories[selectedCategory].products, props.config.settings) + local productsHeight = display.bgCanvas.height - 17 - footerHeight + local heightPerProduct = math.floor(productsHeight / #shopProducts) + local layout + if theme.formatting.layout == "auto" then + if heightPerProduct >= 15 then + layout = "large" + elseif heightPerProduct >= 9 then + layout = "medium" + else + layout = "small" + end + else + layout = theme.formatting.layout + end + + local currency = props.shopState.selectedCurrency + local currencySymbol = renderHelpers.getCurrencySymbol(currency, layout) + while maxAddrWidth == 0 or maxAddrWidth + maxQtyWidth + maxPriceWidth + maxNameWidth > display.bgCanvas.width - 3 do + if props.config.theme.formatting.layout == "auto" and (maxAddrWidth + maxQtyWidth + maxPriceWidth + maxNameWidth > display.bgCanvas.width - 3) then + if layout == "large" then + layout = "medium" + maxAddrWidth = 0 + maxQtyWidth = 0 + maxPriceWidth = 0 + maxNameWidth = 0 + elseif layout == "medium" then + layout = "small" + maxAddrWidth = 0 + maxQtyWidth = 0 + maxPriceWidth = 0 + maxNameWidth = 0 + end + end + currencySymbol = renderHelpers.getCurrencySymbol(currency, layout) + for i = 1, #shopProducts do + local product = shopProducts[i] + local productAddr = product.address .. "@" + if props.shopState.selectedCurrency.name then + if layout == "small" then + if props.config.settings.smallTextKristPayCompatability then + productAddr = product.address .. "@" .. props.shopState.selectedCurrency.name + else + productAddr = product.address .. "@ " + end + end + else + productAddr = product.address + end + product.quantity = product.quantity or 0 + local productPrice = Pricing.getProductPrice(product, props.shopState.selectedCurrency) + if layout == "large" then + maxAddrWidth = math.max(maxAddrWidth, renderHelpers.getWidth(productAddr, layout)+2) + maxQtyWidth = math.max(maxQtyWidth, renderHelpers.getWidth(tostring(product.quantity), layout)+4+2) + maxPriceWidth = math.max(maxPriceWidth, renderHelpers.getWidth(tostring(productPrice) .. currencySymbol, layout)+2) + maxNameWidth = math.max(maxNameWidth, renderHelpers.getWidth(product.name, layout)+2) + elseif layout == "medium" then + maxAddrWidth = math.max(maxAddrWidth, renderHelpers.getWidth(productAddr, layout)+2) + maxQtyWidth = math.max(maxQtyWidth, renderHelpers.getWidth(tostring(product.quantity), layout)+4+2) + maxPriceWidth = math.max(maxPriceWidth, renderHelpers.getWidth(tostring(productPrice) .. currencySymbol, layout)+2) + maxNameWidth = math.max(maxNameWidth, renderHelpers.getWidth(product.name, layout)+2) + else + maxAddrWidth = math.max(maxAddrWidth, renderHelpers.getWidth(productAddr, layout)+1) + maxQtyWidth = math.max(maxQtyWidth, renderHelpers.getWidth(tostring(product.quantity), layout)+2) + maxPriceWidth = math.max(maxPriceWidth, renderHelpers.getWidth(tostring(productPrice) .. currencySymbol, layout)+1) + maxNameWidth = math.max(maxNameWidth, renderHelpers.getWidth(product.name, layout)+1) + end + end + if props.config.theme.formatting.layout ~= "auto" or layout == "small" then + break + end + end + for i = 1, #shopProducts do + local product = shopProducts[i] + -- Display products in format: + --
+ product.quantity = product.quantity or 0 + local productPrice = Pricing.getProductPrice(product, props.shopState.selectedCurrency) + local qtyColor = theme.colors.normalQtyColor + if product.quantity == 0 then + qtyColor = theme.colors.outOfStockQtyColor + elseif product.quantity < 10 then + qtyColor = theme.colors.lowQtyColor + elseif product.quantity < 64 then + qtyColor = theme.colors.warningQtyColor + end + local productNameColor = theme.colors.productNameColor + if product.quantity == 0 then + productNameColor = theme.colors.outOfStockNameColor + end + local productAddr = product.address .. "@" + if props.shopState.selectedCurrency.name then + if layout == "small" then + if props.config.settings.smallTextKristPayCompatability then + productAddr = product.address .. "@" .. props.shopState.selectedCurrency.name + else + productAddr = product.address .. "@ " + end + end + else + productAddr = product.address + end + local kristpayHelperText = props.shopState.selectedCurrency.host + if props.shopState.selectedCurrency.name then + kristpayHelperText = product.address .. "@" .. props.shopState.selectedCurrency.name + end + local productBgColor = theme.colors.productBgColors[((i-1) % #theme.colors.productBgColors) + 1] + if layout == "large" then + table.insert(elements, BigText { + key="qty-"..catName..tostring(product.id), + display=display, + text=tostring(product.quantity), + x=1, + y=16+((i-1)*15), + align="center", + bg=productBgColor, + color=qtyColor, + width=maxQtyWidth + }) + table.insert(elements, BigText { + key="name-"..catName..tostring(product.id), + display=display, + text=product.name, + x=maxQtyWidth+1, + y=16+((i-1)*15), + align=theme.formatting.productNameAlign, + bg=productBgColor, + color=productNameColor, + width=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth-maxQtyWidth + }) + table.insert(elements, BigText { + key="price-"..catName..tostring(product.id), + display=display, + text=tostring(productPrice) .. currencySymbol, + x=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth, + y=16+((i-1)*15), + align="right", + bg=productBgColor, + color=theme.colors.priceColor, + width=maxPriceWidth + }) + table.insert(elements, BigText { + key="addr-"..catName..tostring(product.id), + display=display, + text=productAddr, + x=display.bgCanvas.width-3-maxAddrWidth, + y=16+((i-1)*15), + align="right", + bg=productBgColor, + color=theme.colors.addressColor, + width=maxAddrWidth+4 + }) + table.insert(elements, BasicText { + key="invis-" .. catName .. tostring(product.id), + display=display, + text=kristpayHelperText, + x=1, + y=1+(i*5), + align="center", + bg=productBgColor, + color=productBgColor, + width=#(kristpayHelperText) + }) + elseif layout == "medium" then + table.insert(elements, SmolText { + key="qty-"..catName..tostring(product.id), + display=display, + text=tostring(product.quantity), + x=1, + y=16+((i-1)*9), + align="center", + bg=productBgColor, + color=qtyColor, + width=maxQtyWidth + }) + table.insert(elements, SmolText { + key="name-"..catName..tostring(product.id), + display=display, + text=product.name, + x=maxQtyWidth+1, + y=16+((i-1)*9), + align=theme.formatting.productNameAlign, + bg=productBgColor, + color=productNameColor, + width=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth-maxQtyWidth + }) + table.insert(elements, SmolText { + key="price-"..catName..tostring(product.id), + display=display, + text=tostring(productPrice) .. currencySymbol, + x=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth, + y=16+((i-1)*9), + align="right", + bg=productBgColor, + color=theme.colors.priceColor, + width=maxPriceWidth + }) + table.insert(elements, SmolText { + ey="addr-"..catName..tostring(product.id), + display=display, + text=productAddr, + x=display.bgCanvas.width-3-maxAddrWidth, + y=16+((i-1)*9), + align="right", + bg=productBgColor, + color=theme.colors.addressColor, + width=maxAddrWidth+4 + }) + table.insert(elements, BasicText { + key="invis-" .. catName .. tostring(product.id), + display=display, + text=kristpayHelperText, + x=1, + y=3+(i*3), + align="center", + bg=productBgColor, + color=productBgColor, + width=#(kristpayHelperText) + }) + else + table.insert(elements, BasicText { + key="qty-"..catName..tostring(product.id), + display=display, + text=tostring(product.quantity), + x=1, + y=6+((i-1)*1), + align="center", + bg=productBgColor, + color=qtyColor, + width=maxQtyWidth + }) + table.insert(elements, BasicText { + key="name-"..catName..tostring(product.id), + display=display, + text=product.name, + x=maxQtyWidth+1, + y=6+((i-1)*1), + align=theme.formatting.productNameAlign, + bg=productBgColor, + color=productNameColor, + width=(display.bgCanvas.width/2)-1-maxAddrWidth-maxPriceWidth-maxQtyWidth + }) + table.insert(elements, BasicText { + key="price-"..catName..tostring(product.id), + display=display, + text=tostring(productPrice) .. currencySymbol, + x=(display.bgCanvas.width/2)-1-maxAddrWidth-maxPriceWidth, + y=6+((i-1)*1), + align="right", + bg=productBgColor, + color=theme.colors.priceColor, + width=maxPriceWidth + }) + table.insert(elements, BasicText { + key="addr-"..catName..tostring(product.id), + display=display, + text=productAddr, + x=(display.bgCanvas.width/2)-1-maxAddrWidth, + y=6+((i-1)*1), + align="right", + bg=productBgColor, + color=theme.colors.addressColor, + width=maxAddrWidth+2 + }) + end + end + + local currencyX = 3 + if #props.config.currencies > 1 then + for i = 1, #props.config.currencies do + local symbol = renderHelpers.getCurrencySymbol(props.config.currencies[i], "large") + local symbolSize = bigFont:getWidth(symbol)+6+1 + local bgColor = theme.colors.currencyBgColors[((i-1) % #theme.colors.currencyBgColors) + 1] + table.insert(elements, Button { + display = display, + align = "center", + text = symbol, + x = currencyX, + y = 1, + bg = bgColor, + color = theme.colors.currencyTextColor, + width = symbolSize, + onClick = function() + props.shopState.selectedCurrency = props.config.currencies[i] + props.shopState.lastTouched = os.epoch("utc") + if props.config.settings.playSounds then + sound.playSound(props.speaker, props.config.sounds.button) + end + end + }) + currencyX = currencyX + symbolSize + 2 + end + end + + local categoryX = display.bgCanvas.width - 2 + if #categories > 1 then + for i = #categories, 1, -1 do + local category = categories[i] + local categoryName = category.name + local categoryColor + if i == selectedCategory then + categoryColor = theme.colors.activeCategoryColor + categoryName = "[" .. categoryName .. "]" + else + categoryColor = theme.colors.categoryBgColors[((i-1) % #theme.colors.categoryBgColors) + 1] + end + local categoryWidth = smolFont:getWidth(categoryName)+6 + categoryX = categoryX - categoryWidth - 2 + + table.insert(elements, SmolButton { + display = display, + align = "center", + text = categoryName, + x = categoryX, + y = 4, + bg = categoryColor, + color = theme.colors.categoryTextColor, + width = categoryWidth, + onClick = function() + props.shopState.selectedCategory = i + props.shopState.lastTouched = os.epoch("utc") + if props.config.settings.playSounds then + sound.playSound(props.speaker, props.config.sounds.button) + end + -- canvas:markRect(1, 16, canvas.width, canvas.height-16) + end + }) + end + end + return elements +end + +return render \ No newline at end of file diff --git a/Howlfile.lua b/Howlfile.lua index 3c3cb8b..9157a2c 100644 --- a/Howlfile.lua +++ b/Howlfile.lua @@ -8,7 +8,7 @@ } Tasks:require "main" { - include = {"components/*.lua", "core/*.lua", "fonts/*.lua", "Krypton/*.lua", "modules/*.lua", "res/*.lua", "util/*.lua", "radon.lua", "profile.lua"}, + include = {"components/*.lua", "core/*.lua", "fonts/*.lua", "Krypton/*.lua", "modules/*.lua", "res/*.lua", "util/*.lua", "DefaultLayout.lua", "radon.lua", "profile.lua"}, startup = "radon.lua", output = "build/radon.lua", } diff --git a/components/BasicButton.lua b/components/BasicButton.lua new file mode 100644 index 0000000..96a454f --- /dev/null +++ b/components/BasicButton.lua @@ -0,0 +1,24 @@ +local Solyd = require("modules.solyd") +local hooks = require("modules.hooks") +local useBoundingBox = hooks.useBoundingBox + +local BasicText = require("components.BasicText") + +return Solyd.wrapComponent("BasicButton", function(props) + -- local canvas = Solyd.useContext("canvas") + -- local canvas = useCanvas() + + return BasicText { + display = props.display, + align = props.align, + text = props.text, + x = props.x, + y = props.y, + bg = props.bg, + color = props.color, + width = props.width, + }, { + -- canvas = canvas, + aabb = useBoundingBox((props.x*2)-1, (props.y*3)-2, (props.width or #props.text)*2, 3, props.onClick), + } +end) diff --git a/config.lua b/config.lua index 88d99e7..f5fa6c9 100644 --- a/config.lua +++ b/config.lua @@ -28,7 +28,10 @@ headerAlign = "center", footerAlign = "center", productNameAlign = "center", - layout = "auto" + layout = "auto", -- "auto" automatically picks from "small", "medium", or "large" + -- based on the size of the screen + -- "custom" allows you to specify a custom layout file + --layoutFile = "CardLayout.lua" }, colors = { bgColor = colors.lightGray, @@ -80,6 +83,56 @@ [colors.pink] = 0xFF77A9, [colors.lightBlue] = 0x3D7EDB, [colors.white] = 0xECECEC + }, + layouts = { + ["CardLayout.lua"] = { + -- Special theming for the card layout + -- Takes precedence over the above settings + settings = { + showCredits = true + }, + colors = { + creditsColor = colors.lightGray, + bgColor = colors.gray, + headerColor = colors.lime, + headerSuffixColor = colors.lightGray, + subheaderBgColor = colors.white, + subheaderColor = colors.black, + footerBgColor = colors.black, + footerColor = colors.white, + dividerColor = colors.black, + productNameColor = colors.lime, + fieldLabelColor = colors.gray, + normalQtyColor = colors.black, + addressColor = colors.black, + priceBgColor = colors.lime, + priceColor = colors.white, + productBgColors = { + colors.lightGray + }, + productBgShadowColors = { + colors.black + }, + }, + palette = { + [colors.black] = 0x111111, + [colors.blue] = 0x3366cc, + [colors.purple] = 0xb266e5, + [colors.green] = 0x57a64e, + [colors.brown] = 0x7f664c, + [colors.gray] = 0x4c4c4c, + [colors.lightGray] = 0x999999, + [colors.red] = 0xcc4c4c, + [colors.orange] = 0xf2b233, + [colors.yellow] = 0xdede6c, + [colors.lime] = 0x7fcc19, + [colors.cyan] = 0x4c99b2, + [colors.magenta] = 0xe57fd8, + [colors.pink] = 0xf2b2cc, + [colors.lightBlue] = 0x99b2f2, + [colors.white] = 0xf0f0f0 + } + } } }, sounds = { diff --git a/core/ConfigValidator.lua b/core/ConfigValidator.lua index 191120a..0eecf3a 100644 --- a/core/ConfigValidator.lua +++ b/core/ConfigValidator.lua @@ -29,7 +29,8 @@ headerAlign = "enum<'left' | 'center' | 'right'>: alignment", footerAlign = "enum<'left' | 'center' | 'right'>: alignment", productNameAlign = "enum<'left' | 'center' | 'right'>: alignment", - layout = "enum<'small' | 'medium' | 'large' | 'auto'>: layout", + layout = "enum<'small' | 'medium' | 'large' | 'auto' | 'custom'>: layout", + layoutFile = "file?" }, colors = { bgColor = "color", @@ -174,6 +175,14 @@ if entryType == "function" and type(value) ~= "function" then error("Config value " .. subpath .. " must be a function") end + if entryType == "file" then + if type(value) ~= "string" then + error("Config value " .. subpath .. " must be a file") + end + if not fs.exists(value) or fs.isDir(value) then + error("Config value " .. subpath .. " must refer to a file") + end + end if entryType == "color" then if type(value) ~= "number" then error("Config value " .. subpath .. " must be a color") diff --git a/radon.lua b/radon.lua index 78722ef..b530a3e 100644 --- a/radon.lua +++ b/radon.lua @@ -1,7 +1,7 @@ local oldPullEvent = os.pullEvent os.pullEvent = os.pullEventRaw -local version = "1.1.11" +local version = "1.2.0" --- Imports local _ = require("util.score") @@ -28,6 +28,8 @@ local ShopRunner = require("core.ShopRunner") local ConfigValidator = require("core.ConfigValidator") +local defaultLayout = require("DefaultLayout") + local loadRIF = require("modules.rif") local config = require("config") @@ -77,489 +79,39 @@ local display = Display.new({theme=config.theme, monitor=config.peripherals.monitor}) -local function getDisplayedProducts(allProducts, settings) - local displayedProducts = {} - for i = 1, #allProducts do - local product = allProducts[i] - product.id = i - if not settings.hideUnavailableProducts or product.quantity > 0 then - table.insert(displayedProducts, product) - end - end - return displayedProducts -end - -local function getCurrencySymbol(currency, layout) - if currency.krypton and currency.krypton.currency then - currencySymbol = currency.krypton.currency.currency_symbol - elseif not currencySymbol and currency.name and currency.name:find("%.") then - currencySymbol = currency.name:sub(currency.name:find("%.")+1, #currency.name) - elseif currency.id == "tenebra" then - currencySymbol = "tst" - else - currencySymbol = "KST" - end - if currencySymbol == "TST" then - currencySymbol = "tst" - end - if currencySymbol:lower() == "kst" and layout == "medium" then - currencySymbol = "kst" - elseif currencySymbol:lower() == "kst" then - currencySymbol = "\164" - end - return currencySymbol -end - -local function getCategories(products) - local categories = {} - for _, product in ipairs(products) do - local category = product.category - if not category then - category = "*" - end - local found = nil - for i = 1, #categories do - if categories[i].name == category then - found = i - break - end - end - if not found then - if category == "*" then - table.insert(categories, 1, {name=category, products={}}) - found = 1 - else - table.insert(categories, {name=category, products={}}) - found = #categories - end - end - table.insert(categories[found].products, product) - end - return categories -end - -local function getWidth(text, fontSize) - if fontSize == "large" then - return bigFont:getWidth(text) - elseif fontSize == "medium" then - return smolFont:getWidth(text) - else - return #text - end -end - +local layoutFile = nil +local layoutRenderer = nil local Main = Solyd.wrapComponent("Main", function(props) local canvas = useCanvas(display) local theme = props.config.theme - + local flatCanvas = {} - - local categories = getCategories(props.shopState.products) - local selectedCategory = props.shopState.selectedCategory - - local currencyEndX = 3 - if #props.config.currencies > 1 then - for i = 1, #props.config.currencies do - local symbol = getCurrencySymbol(props.config.currencies[i], "large") - local symbolSize = bigFont:getWidth(symbol)+6 - currencyEndX = currencyEndX + symbolSize + 2 - end - end - - local categoryX = display.bgCanvas.width - 2 - if #categories > 1 then - for i = #categories, 1, -1 do - local category = categories[i] - local categoryName = category.name - if i == selectedCategory then - categoryName = "[" .. categoryName .. "]" - end - local categoryWidth = smolFont:getWidth(categoryName)+6 - categoryX = categoryX - categoryWidth - 2 - end - end - - local headerCx = math.floor((display.bgCanvas.width - bigFont:getWidth(props.config.branding.title)) / 2) - local header - -- TODO: Change header font size based on width - if theme.formatting.headerAlign == "center" and headerCx < currencyEndX and #categories == 1 then - table.insert(flatCanvas, Rect { display=display, x=1, y=1, width=currencyEndX, height=bigFont.height+6, color=theme.colors.headerBgColor }) - header = BigText { display=display, text=props.config.branding.title, x=currencyEndX, y=1, align="left", bg=theme.colors.headerBgColor, color = theme.colors.headerColor, width=display.bgCanvas.width } - elseif theme.formatting.headerAlign == "center" and headerCx+bigFont:getWidth(props.config.branding.title) > categoryX and #categories > 1 then - table.insert(flatCanvas, Rect { display=display, x=categoryX, y=1, width=display.bgCanvas.width-categoryX+1, height=bigFont.height+6, color=theme.colors.headerBgColor }) - header = BigText { display=display, text=props.config.branding.title, x=1, y=1, align="right", bg=theme.colors.headerBgColor, color = theme.colors.headerColor, width=categoryX-1 } + if theme.formatting.layout ~= "custom" or not theme.formatting.layoutFile then + flatCanvas = defaultLayout(canvas, display, props, theme, version) else - header = BigText { display=display, text=props.config.branding.title, x=1, y=1, align=theme.formatting.headerAlign, bg=theme.colors.headerBgColor, color = theme.colors.headerColor, width=display.bgCanvas.width } - end - - table.insert(flatCanvas, header) - - local footerHeight = 0 - if props.config.settings.showFooter then - local footerMessage - if props.shopState.selectedCurrency.name or not props.config.lang.footerNoName then - footerMessage = props.config.lang.footer - else - footerMessage = props.config.lang.footerNoName - end - if props.shopState.selectedCurrency.name and footerMessage:find("%%name%%") then - footerMessage = footerMessage:gsub("%%name%%", props.shopState.selectedCurrency.name) - end - if footerMessage:find("%%addr%%") then - footerMessage = footerMessage:gsub("%%addr%%", props.shopState.selectedCurrency.host) - end - if footerMessage:find("%%version%%") then - footerMessage = footerMessage:gsub("%%version%%", version) - end - - if props.shopState.selectedCurrency then - local footer - if smolFont:getWidth(footerMessage) < display.bgCanvas.width then - footer = SmolText { display=display, text=footerMessage, x=1, y=display.bgCanvas.height-smolFont.height-4, align=theme.formatting.footerAlign, bg=theme.colors.footerBgColor, color = theme.colors.footerColor, width=display.bgCanvas.width } - else - footer = BasicText { display=display, text=footerMessage, x=1, y=math.floor(display.bgCanvas.height/3), align=theme.formatting.footerAlign, bg=theme.colors.footerBgColor, color = theme.colors.footerColor, width=math.ceil(display.bgCanvas.width/2) } + if theme.formatting.layoutFile ~= layoutFile then + layoutFile = theme.formatting.layoutFile + local f = fs.open(layoutFile, "r") + if not f then + error("Could not open layout file: " .. layoutFile) end - - table.insert(flatCanvas, footer) - end - footerHeight = smolFont.height + 4 - end - - local maxAddrWidth = 0 - local maxQtyWidth = 0 - local maxPriceWidth = 0 - local maxNameWidth = 0 - props.shopState.numCategories = #categories - local catName = categories[selectedCategory].name - local shopProducts = getDisplayedProducts(categories[selectedCategory].products, config.settings) - local productsHeight = display.bgCanvas.height - 17 - footerHeight - local heightPerProduct = math.floor(productsHeight / #shopProducts) - local layout - if theme.formatting.layout == "auto" then - if heightPerProduct >= 15 then - layout = "large" - elseif heightPerProduct >= 9 then - layout = "medium" - else - layout = "small" - end - else - layout = theme.formatting.layout - end - - local currency = props.shopState.selectedCurrency - local currencySymbol = getCurrencySymbol(currency, layout) - while maxAddrWidth == 0 or maxAddrWidth + maxQtyWidth + maxPriceWidth + maxNameWidth > display.bgCanvas.width - 3 do - if props.config.theme.formatting.layout == "auto" and (maxAddrWidth + maxQtyWidth + maxPriceWidth + maxNameWidth > display.bgCanvas.width - 3) then - if layout == "large" then - layout = "medium" - maxAddrWidth = 0 - maxQtyWidth = 0 - maxPriceWidth = 0 - maxNameWidth = 0 - elseif layout == "medium" then - layout = "small" - maxAddrWidth = 0 - maxQtyWidth = 0 - maxPriceWidth = 0 - maxNameWidth = 0 + layoutRendererString = f.readAll() + f.close() + local loadedString, err = load(layoutRendererString, layoutFile, "t", setmetatable({ require = require }, {__index = _ENV})) + if not loadedString then + error("Could not load layout file: " .. err) end - end - currencySymbol = getCurrencySymbol(currency, layout) - for i = 1, #shopProducts do - local product = shopProducts[i] - local productAddr = product.address .. "@" - if props.shopState.selectedCurrency.name then - if layout == "small" then - if props.config.settings.smallTextKristPayCompatability then - productAddr = product.address .. "@" .. props.shopState.selectedCurrency.name - else - productAddr = product.address .. "@ " - end - end - else - productAddr = product.address + layoutRenderer = loadedString() + if theme.layouts[layoutFile] and theme.layouts[layoutFile].palette then + require("util.setPalette")(display.mon, theme.layouts[layoutFile].palette) end - product.quantity = product.quantity or 0 - local productPrice = Pricing.getProductPrice(product, props.shopState.selectedCurrency) - if layout == "large" then - maxAddrWidth = math.max(maxAddrWidth, getWidth(productAddr, layout)+2) - maxQtyWidth = math.max(maxQtyWidth, getWidth(tostring(product.quantity), layout)+4+2) - maxPriceWidth = math.max(maxPriceWidth, getWidth(tostring(productPrice) .. currencySymbol, layout)+2) - maxNameWidth = math.max(maxNameWidth, getWidth(product.name, layout)+2) - elseif layout == "medium" then - maxAddrWidth = math.max(maxAddrWidth, getWidth(productAddr, layout)+2) - maxQtyWidth = math.max(maxQtyWidth, getWidth(tostring(product.quantity), layout)+4+2) - maxPriceWidth = math.max(maxPriceWidth, getWidth(tostring(productPrice) .. currencySymbol, layout)+2) - maxNameWidth = math.max(maxNameWidth, getWidth(product.name, layout)+2) - else - maxAddrWidth = math.max(maxAddrWidth, getWidth(productAddr, layout)+1) - maxQtyWidth = math.max(maxQtyWidth, getWidth(tostring(product.quantity), layout)+2) - maxPriceWidth = math.max(maxPriceWidth, getWidth(tostring(productPrice) .. currencySymbol, layout)+1) - maxNameWidth = math.max(maxNameWidth, getWidth(product.name, layout)+1) + if theme.layouts[layoutFile] and theme.layouts[layoutFile].colors and theme.layouts[layoutFile].colors.bgColor then + display.ccCanvas.clear = theme.layouts[layoutFile].colors.bgColor end + display.ccCanvas:outputFlush(display.mon) end - if props.config.theme.formatting.layout ~= "auto" or layout == "small" then - break - end - end - for i = 1, #shopProducts do - local product = shopProducts[i] - -- Display products in format: - --
- product.quantity = product.quantity or 0 - local productPrice = Pricing.getProductPrice(product, props.shopState.selectedCurrency) - local qtyColor = theme.colors.normalQtyColor - if product.quantity == 0 then - qtyColor = theme.colors.outOfStockQtyColor - elseif product.quantity < 10 then - qtyColor = theme.colors.lowQtyColor - elseif product.quantity < 64 then - qtyColor = theme.colors.warningQtyColor - end - local productNameColor = theme.colors.productNameColor - if product.quantity == 0 then - productNameColor = theme.colors.outOfStockNameColor - end - local productAddr = product.address .. "@" - if props.shopState.selectedCurrency.name then - if layout == "small" then - if props.config.settings.smallTextKristPayCompatability then - productAddr = product.address .. "@" .. props.shopState.selectedCurrency.name - else - productAddr = product.address .. "@ " - end - end - else - productAddr = product.address - end - local kristpayHelperText = props.shopState.selectedCurrency.host - if props.shopState.selectedCurrency.name then - kristpayHelperText = product.address .. "@" .. props.shopState.selectedCurrency.name - end - local productBgColor = theme.colors.productBgColors[((i-1) % #theme.colors.productBgColors) + 1] - if layout == "large" then - table.insert(flatCanvas, BigText { - key="qty-"..catName..tostring(product.id), - display=display, - text=tostring(product.quantity), - x=1, - y=16+((i-1)*15), - align="center", - bg=productBgColor, - color=qtyColor, - width=maxQtyWidth - }) - table.insert(flatCanvas, BigText { - key="name-"..catName..tostring(product.id), - display=display, - text=product.name, - x=maxQtyWidth+1, - y=16+((i-1)*15), - align=theme.formatting.productNameAlign, - bg=productBgColor, - color=productNameColor, - width=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth-maxQtyWidth - }) - table.insert(flatCanvas, BigText { - key="price-"..catName..tostring(product.id), - display=display, - text=tostring(productPrice) .. currencySymbol, - x=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth, - y=16+((i-1)*15), - align="right", - bg=productBgColor, - color=theme.colors.priceColor, - width=maxPriceWidth - }) - table.insert(flatCanvas, BigText { - key="addr-"..catName..tostring(product.id), - display=display, - text=productAddr, - x=display.bgCanvas.width-3-maxAddrWidth, - y=16+((i-1)*15), - align="right", - bg=productBgColor, - color=theme.colors.addressColor, - width=maxAddrWidth+4 - }) - table.insert(flatCanvas, BasicText { - key="invis-" .. catName .. tostring(product.id), - display=display, - text=kristpayHelperText, - x=1, - y=1+(i*5), - align="center", - bg=productBgColor, - color=productBgColor, - width=#(kristpayHelperText) - }) - elseif layout == "medium" then - table.insert(flatCanvas, SmolText { - key="qty-"..catName..tostring(product.id), - display=display, - text=tostring(product.quantity), - x=1, - y=16+((i-1)*9), - align="center", - bg=productBgColor, - color=qtyColor, - width=maxQtyWidth - }) - table.insert(flatCanvas, SmolText { - key="name-"..catName..tostring(product.id), - display=display, - text=product.name, - x=maxQtyWidth+1, - y=16+((i-1)*9), - align=theme.formatting.productNameAlign, - bg=productBgColor, - color=productNameColor, - width=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth-maxQtyWidth - }) - table.insert(flatCanvas, SmolText { - key="price-"..catName..tostring(product.id), - display=display, - text=tostring(productPrice) .. currencySymbol, - x=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth, - y=16+((i-1)*9), - align="right", - bg=productBgColor, - color=theme.colors.priceColor, - width=maxPriceWidth - }) - table.insert(flatCanvas, SmolText { - ey="addr-"..catName..tostring(product.id), - display=display, - text=productAddr, - x=display.bgCanvas.width-3-maxAddrWidth, - y=16+((i-1)*9), - align="right", - bg=productBgColor, - color=theme.colors.addressColor, - width=maxAddrWidth+4 - }) - table.insert(flatCanvas, BasicText { - key="invis-" .. catName .. tostring(product.id), - display=display, - text=kristpayHelperText, - x=1, - y=3+(i*3), - align="center", - bg=productBgColor, - color=productBgColor, - width=#(kristpayHelperText) - }) - else - table.insert(flatCanvas, BasicText { - key="qty-"..catName..tostring(product.id), - display=display, - text=tostring(product.quantity), - x=1, - y=6+((i-1)*1), - align="center", - bg=productBgColor, - color=qtyColor, - width=maxQtyWidth - }) - table.insert(flatCanvas, BasicText { - key="name-"..catName..tostring(product.id), - display=display, - text=product.name, - x=maxQtyWidth+1, - y=6+((i-1)*1), - align=theme.formatting.productNameAlign, - bg=productBgColor, - color=productNameColor, - width=(display.bgCanvas.width/2)-1-maxAddrWidth-maxPriceWidth-maxQtyWidth - }) - table.insert(flatCanvas, BasicText { - key="price-"..catName..tostring(product.id), - display=display, - text=tostring(productPrice) .. currencySymbol, - x=(display.bgCanvas.width/2)-1-maxAddrWidth-maxPriceWidth, - y=6+((i-1)*1), - align="right", - bg=productBgColor, - color=theme.colors.priceColor, - width=maxPriceWidth - }) - table.insert(flatCanvas, BasicText { - key="addr-"..catName..tostring(product.id), - display=display, - text=productAddr, - x=(display.bgCanvas.width/2)-1-maxAddrWidth, - y=6+((i-1)*1), - align="right", - bg=productBgColor, - color=theme.colors.addressColor, - width=maxAddrWidth+2 - }) - end - end - - local currencyX = 3 - if #props.config.currencies > 1 then - for i = 1, #props.config.currencies do - local symbol = getCurrencySymbol(props.config.currencies[i], "large") - local symbolSize = bigFont:getWidth(symbol)+6+1 - local bgColor = theme.colors.currencyBgColors[((i-1) % #theme.colors.currencyBgColors) + 1] - table.insert(flatCanvas, Button { - display = display, - align = "center", - text = symbol, - x = currencyX, - y = 1, - bg = bgColor, - color = theme.colors.currencyTextColor, - width = symbolSize, - onClick = function() - props.shopState.selectedCurrency = props.config.currencies[i] - props.shopState.lastTouched = os.epoch("utc") - if config.settings.playSounds then - sound.playSound(speaker, config.sounds.button) - end - end - }) - currencyX = currencyX + symbolSize + 2 - end - end - - local categoryX = display.bgCanvas.width - 2 - if #categories > 1 then - for i = #categories, 1, -1 do - local category = categories[i] - local categoryName = category.name - local categoryColor - if i == selectedCategory then - categoryColor = theme.colors.activeCategoryColor - categoryName = "[" .. categoryName .. "]" - else - categoryColor = theme.colors.categoryBgColors[((i-1) % #theme.colors.categoryBgColors) + 1] - end - local categoryWidth = smolFont:getWidth(categoryName)+6 - categoryX = categoryX - categoryWidth - 2 - - table.insert(flatCanvas, SmolButton { - display = display, - align = "center", - text = categoryName, - x = categoryX, - y = 4, - bg = categoryColor, - color = theme.colors.categoryTextColor, - width = categoryWidth, - onClick = function() - props.shopState.selectedCategory = i - props.shopState.lastTouched = os.epoch("utc") - if config.settings.playSounds then - sound.playSound(speaker, config.sounds.button) - end - -- canvas:markRect(1, 16, canvas.width, canvas.height-16) - end - }) - end + flatCanvas = layoutRenderer(canvas, display, props, theme, version) end return _.flat({ _.flat(flatCanvas) }), { @@ -641,7 +193,7 @@ eventHook.execute(config.hooks.start, version, config, products) end while true do - tree = Solyd.render(tree, Main {t = t, config = config, shopState = shopState}) + tree = Solyd.render(tree, Main {t = t, config = config, shopState = shopState, speaker = speaker}) local context = Solyd.getTopologicalContext(tree, { "canvas", "aabb" }) diff --git a/util/renderHelpers.lua b/util/renderHelpers.lua new file mode 100644 index 0000000..303f1ea --- /dev/null +++ b/util/renderHelpers.lua @@ -0,0 +1,110 @@ +local bigFont = require("fonts.bigfont") +local smolFont = require("fonts.smolfont") + +local function getDisplayedProducts(allProducts, settings) + local displayedProducts = {} + for i = 1, #allProducts do + local product = allProducts[i] + product.id = i + if not settings.hideUnavailableProducts or product.quantity > 0 then + table.insert(displayedProducts, product) + end + end + return displayedProducts +end + +local function getCurrencySymbol(currency, layout) + if currency.krypton and currency.krypton.currency then + currencySymbol = currency.krypton.currency.currency_symbol + elseif not currencySymbol and currency.name and currency.name:find("%.") then + currencySymbol = currency.name:sub(currency.name:find("%.")+1, #currency.name) + elseif currency.id == "tenebra" then + currencySymbol = "tst" + else + currencySymbol = "KST" + end + if currencySymbol == "TST" then + currencySymbol = "tst" + end + if currencySymbol:lower() == "kst" and (layout == "medium" or layout == "small") then + currencySymbol = "kst" + elseif currencySymbol:lower() == "kst" then + currencySymbol = "\164" + end + return currencySymbol +end + +local function getCategories(products) + local categories = {} + for _, product in ipairs(products) do + local category = product.category + if not category then + category = "*" + end + local found = nil + for i = 1, #categories do + if categories[i].name == category then + found = i + break + end + end + if not found then + if category == "*" then + table.insert(categories, 1, {name=category, products={}}) + found = 1 + else + table.insert(categories, {name=category, products={}}) + found = #categories + end + end + table.insert(categories[found].products, product) + end + return categories +end + +local function getWidth(text, fontSize) + if fontSize == "large" then + return bigFont:getWidth(text) + elseif fontSize == "medium" then + return smolFont:getWidth(text) + else + return #text + end +end + +local function getThemeSetting(theme, path, layout) + -- Split on "." + layoutTheme = {} + if theme.layouts and theme.layouts[layout] then + layoutTheme = theme.layouts[layout] + end + local paths = path:gmatch("[^%.]+") + for subpath in paths do + if layoutTheme and layoutTheme[subpath] then + layoutTheme = layoutTheme[subpath] + else + layoutTheme = nil + end + if theme and theme[subpath] then + theme = theme[subpath] + else + theme = nil + end + if not theme and not layoutTheme then + return nil + end + end + if layoutTheme then + return layoutTheme + else + return theme + end +end + +return { + getDisplayedProducts = getDisplayedProducts, + getCurrencySymbol = getCurrencySymbol, + getCategories = getCategories, + getWidth = getWidth, + getThemeSetting = getThemeSetting, +} \ No newline at end of file