- major code reorganization into multiple classes
- added support for pixel-based unit of measurement
- further simplified some "black magic" constants
- implemented margins to simulate container element
- revamped translation interface for stateful use
- various improvements to element parameter parsing
This commit is contained in:
Leslie Krause 2019-05-22 19:38:31 -04:00
parent 24912c357e
commit 019c412d42
2 changed files with 430 additions and 374 deletions

View File

@ -1,4 +1,4 @@
Scarlet Mod v1.0 Scarlet Mod v1.1
By Leslie Krause By Leslie Krause
Scarlet is a thin-wrapper library for Minetest that provides a logical, uniform system of Scarlet is a thin-wrapper library for Minetest that provides a logical, uniform system of
@ -20,6 +20,14 @@ Revision History
Version 1.0b (18-May-2019) Version 1.0b (18-May-2019)
- initial alpha version - initial alpha version
Version 1.1b (22-May-2019)
- major code reorganization into multiple classes
- added support for pixel-based unit of measurement
- further simplified some "black magic" constants
- implemented margins to simulate container element
- revamped translation interface for stateful use
- various improvements to element parameter parsing
Compatability Compatability
---------------------- ----------------------
@ -44,7 +52,7 @@ Source Code License
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018, Leslie Krause (leslie@searstower.org) Copyright (c) 2019, Leslie Krause (leslie@searstower.org)
Permission is hereby granted, free of charge, to any person obtaining a copy of this Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software software and associated documentation files (the "Software"), to deal in the Software

532
init.lua
View File

@ -5,474 +5,528 @@
-- Copyright (c) 2019, Leslie E. Krause -- Copyright (c) 2019, Leslie E. Krause
-------------------------------------------------------- --------------------------------------------------------
ScarletDef = function ( screen_dpi, gui_scaling, has_padding ) scarlet = { }
------------------------
-- UnitConversion Class
------------------------
function UnitConversion( screen_dpi, gui_scaling, has_padding )
local self = { }
local image_size = 0.55 * ( screen_dpi or 72 ) * ( gui_scaling or 1 ) local image_size = 0.55 * ( screen_dpi or 72 ) * ( gui_scaling or 1 )
-- calculate the dot pitch as images per pixel and cells per pixel -- calculate the dot pitch as images per pixel and cells per pixel
-- using the "black magic" algorithm from guiFormSpecMenu.cpp -- using the "black magic" algorithm from guiFormSpecMenu.cpp
local dot_pitch = {
self.dot_pitch = {
-- imgsize (images per pixel) -- imgsize (images per pixel)
ix = 1 / image_size, ix = 1 / image_size,
iy = 1 / image_size, iy = 1 / image_size,
-- spacing (cells per pixel) -- spacing (cells per pixel)
x = 1 / ( image_size * 5 / 4 ), x = 1 / image_size * 4 / 5,
y = 1 / ( image_size * 15 / 13 ) y = 1 / image_size * 13 / 15
} }
-- padding width and height in cell units -- padding width and height in cell units
-- original formula: image_size * 3.0 / 8 -- original formula: image_size * 3.0 / 8
local padding_width = has_padding and 0 or 3 / 10 self.padding_width = has_padding and 0.0 or 3 / 10
local padding_height = has_padding and 0 or 13 / 40 self.padding_height = has_padding and 0.0 or 13 / 40
-- nominal button height in cell units -- nominal button height in cell units
-- NB: this is 2x the value of m_button_height used internally! -- NB: this is 2x the value of m_button_height used internally!
-- original formula: image_size * 15.0 / 13 * 0.35 -- original formula: image_size * 15.0 / 13 * 0.35
local button_height = 0.7 self.button_height = 0.7
-- cell margin width and height in cell units -- cell margin width and height in cell units
-- original formula: vx * spacing.x - ( spacing.x - imgsize.x ) -- original formula: vx * spacing.x - ( spacing.x - imgsize.x )
local cell_margin_width = 1 / 5 self.cell_margin_width = 1 / 5
local cell_margin_height = 2 / 15 self.cell_margin_height = 2 / 15
--------------------------- self.units = ( function ( )
-- unit conversion objects
---------------------------
local units = ( function ( )
-- cell size measurements -- cell size measurements
local factors = { local factors = {
d = { x = 1 / 39.6 * 4 / 5, y = 1 / 39.6 * 13 / 15 },
i = { x = 4 / 5, y = 13 / 15 }, -- imgsize i = { x = 4 / 5, y = 13 / 15 }, -- imgsize
c = { x = 1, y = 1 }, -- spacing (unity) c = { x = 1, y = 1 }, -- spacing (unity)
b = { y = button_height }, -- 2 x m_button_height b = { y = self.button_height }, -- 2 x m_button_height
} }
local function get_x( v, u ) local function get_x( v, u, dot_pitch )
return not factors[ u ] and math.floor( v ) * dot_pitch.x or v * factors[ u ].x return not factors[ u ] and math.floor( v ) * self.dot_pitch.x or v * factors[ u ].x
end end
local function get_y( v, u ) local function get_y( v, u, dot_pitch )
return not factors[ u ] and math.floor( v ) * dot_pitch.y or v * factors[ u ].y return not factors[ u ] and math.floor( v ) * self.dot_pitch.y or v * factors[ u ].y
end end
return { get_x = get_x, get_y = get_y } return { get_x = get_x, get_y = get_y }
end )( ) end )( )
local iunits = ( function ( ) self.iunits = ( function ( )
-- image size measurements -- image size measurements
local factors = { local factors = {
d = { x = 1 / 39.6, y = 1 / 39.6 },
i = { x = 1, y = 1 }, -- imgsize (unity) i = { x = 1, y = 1 }, -- imgsize (unity)
c = { x = 5 / 4, y = 15 / 13 }, -- spacing c = { x = 5 / 4, y = 15 / 13 }, -- spacing
b = { y = button_height * 15 / 13 }, -- 2 x m_button_height b = { y = self.button_height * 15 / 13 }, -- 2 x m_button_height
} }
local function get_x( v, u ) local function get_x( v, u )
return not factors[ u ] and math.floor( v ) * dot_pitch.ix or v * factors[ u ].x return not factors[ u ] and math.floor( v ) * self.dot_pitch.ix or v * factors[ u ].x
end end
local function get_y( v, u ) local function get_y( v, u )
return not factors[ u ] and math.floor( v ) * dot_pitch.iy or v * factors[ u ].y return not factors[ u ] and math.floor( v ) * self.dot_pitch.iy or v * factors[ u ].y
end end
return { get_x = get_x, get_y = get_y } return { get_x = get_x, get_y = get_y }
end )( ) end )( )
----------------------- return self
-- translation helpers end
-----------------------
local function element( name, params ) ---------------------------
return name .. "[" .. table.concat( params, ";" ) .. "]" -- RuntimeTranslator Class
---------------------------
function RuntimeTranslator( screen_dpi, gui_scaling, has_padding )
local self = UnitConversion( screen_dpi, gui_scaling, has_padding ) -- extend the base class
self.margins = { { width = 0.0, height = 0.0 } }
self.insert_margin = function ( width, height )
table.insert( self.margins, 1,
{ width = width + self.margins[ 1 ].width, height = height + self.margins[ 1 ].height }
)
end end
local function get_pos( params, pos_units, offsets ) self.remove_margin = function ( )
local replace = "%0.3f,%0.3f" if #self.margins > 1 then
local pos_x, u1, pos_y, u2 = string.match( params[ 1 ], "^(-?[0-9.]+)([icp]?),(-?[0-9.]+)([icpb]?)$" ) table.remove( self.margins, 1 )
end
end
self.get_pos = function ( pos_value, pos_units, offsets )
if pos_value then
local pos_x, u1, pos_y, u2 = string.match( pos_value, "^(-?[0-9.]+)([icpd]?),(-?[0-9.]+)([icpdb]?)$" )
if not pos_x then return end if not pos_x then return end
return string.format( replace, return string.format( "%0.3f,%0.3f",
pos_units.get_x( pos_x, u1 ) + offsets[ 1 ], pos_units.get_x( pos_x, u1 ) + offsets[ 1 ] + self.margins[ 1 ].width,
pos_units.get_y( pos_y, u2 ) + offsets[ 2 ] pos_units.get_y( pos_y, u2 ) + offsets[ 2 ] + self.margins[ 1 ].height
) )
end end
end
local function get_pos_and_dim_x( params, pos_units, dim_units, offsets ) self.get_pos_and_dim_x = function ( pos_value, dim_value, pos_units, dim_units, offsets )
local replace = "%0.3f,%0.3f;%0.3f,0.000" if pos_value and dim_value then
local pos_x, u1, pos_y, u2 = string.match( params[ 1 ], "^(-?[0-9.]+)([icp]?),(-?[0-9.]+)([icpb]?)$" ) local pos_x, u1, pos_y, u2 = string.match( pos_value, "^(-?[0-9.]+)([icpd]?),(-?[0-9.]+)([icpdb]?)$" )
local dim_x, u3 = string.match( params[ 2 ], "^([0-9.]+)([iscp]?)$" ) local dim_x, u3 = string.match( dim_value, "^([0-9.]+)([iscp]?)$" )
if not pos_x or not dim_x then return end if not pos_x or not dim_x then return end
if u3 == "s" then if u3 == "s" then
u3 = "i" u3 = "i"
offsets[ 3 ] = offsets[ 3 ] + ( dim_x % 1 == 0 and dim_x - 1 or math.floor( dim_x ) ) * cell_margin_width offsets[ 3 ] = offsets[ 3 ] + ( dim_x % 1 == 0 and dim_x - 1 or math.floor( dim_x ) ) * self.cell_margin_width
end end
return string.format( replace, return string.format( "%0.3f,%0.3f;%0.3f,0.000",
pos_units.get_x( pos_x, u1 ) + offsets[ 1 ], pos_units.get_x( pos_x, u1 ) + offsets[ 1 ] + self.margins[ 1 ].width,
pos_units.get_y( pos_y, u2 ) + offsets[ 2 ], pos_units.get_y( pos_y, u2 ) + offsets[ 2 ] + self.margins[ 1 ].height,
dim_units.get_x( dim_x, u3 ) + offsets[ 3 ] dim_units.get_x( dim_x, u3 ) + offsets[ 3 ]
) )
end end
end
local function get_pos_and_dim( params, pos_units, dim_units, offsets ) self.get_pos_and_dim = function ( pos_value, dim_value, pos_units, dim_units, offsets )
local replace = "%0.3f,%0.3f;%0.3f,%0.3f" if pos_value and dim_value then
local pos_x, u1, pos_y, u2 = string.match( params[ 1 ], "^(-?[0-9.]+)([icp]?),(-?[0-9.]+)([icpb]?)$" ) local pos_x, u1, pos_y, u2 = string.match( pos_value, "^(-?[0-9.]+)([icpd]?),(-?[0-9.]+)([icpdb]?)$" )
local dim_x, u3, dim_y, u4 = string.match( params[ 2 ], "^([0-9.]+)([iscp]?),([0-9.]+)([iscpb]?)$" ) local dim_x, u3, dim_y, u4 = string.match( dim_value, "^([0-9.]+)([iscpd]?),([0-9.]+)([iscpdb]?)$" )
if not pos_x or not dim_x then return end if not pos_x or not dim_x then return end
if u3 == "s" then if u3 == "s" then
u3 = "i" u3 = "i"
offsets[ 3 ] = offsets[ 3 ] + ( dim_x % 1 == 0 and dim_x - 1 or math.floor( dim_x ) ) * cell_margin_width offsets[ 3 ] = offsets[ 3 ] + ( dim_x % 1 == 0 and dim_x - 1 or math.floor( dim_x ) ) * self.cell_margin_width
end end
if u4 == "s" then if u4 == "s" then
u4 = "i" u4 = "i"
offsets[ 4 ] = offsets[ 4 ] + ( dim_y % 1 == 0 and dim_y - 1 or math.floor( dim_y ) ) * cell_margin_height offsets[ 4 ] = offsets[ 4 ] + ( dim_y % 1 == 0 and dim_y - 1 or math.floor( dim_y ) ) * self.cell_margin_height
end end
return string.format( replace, return string.format( "%0.3f,%0.3f;%0.3f,%0.3f",
pos_units.get_x( pos_x, u1 ) + offsets[ 1 ], pos_units.get_x( pos_x, u1 ) + offsets[ 1 ] + self.margins[ 1 ].width,
pos_units.get_y( pos_y, u2 ) + offsets[ 2 ], pos_units.get_y( pos_y, u2 ) + offsets[ 2 ] + self.margins[ 1 ].height,
dim_units.get_x( dim_x, u3 ) + offsets[ 3 ], dim_units.get_x( dim_x, u3 ) + offsets[ 3 ],
dim_units.get_y( dim_y, u4 ) + offsets[ 4 ] dim_units.get_y( dim_y, u4 ) + offsets[ 4 ]
) )
end end
end
------------------------------ return self
-- generic element subclasses end
------------------------------
local function ElemPos( pos_units, length ) ------------------------------
return function ( name, params ) -- Generic Element Subclasses
local pos = get_pos( params, pos_units, { ------------------------------
-padding_width,
-padding_height local function element( name, params )
return name .. "[" .. table.concat( params, ";" ) .. "]"
end
local function ElemPos( pos_units, length )
return function ( tx, name, params )
local pos = tx.get_pos( params[ 1 ], pos_units, {
-tx.padding_width,
-tx.padding_height
} ) } )
assert( pos and #params == length, "Cannot parse formspec element " .. name .. "[]" ) assert( pos and #params == length, "Cannot parse formspec element " .. name .. "[]" )
return element( name, { pos, unpack( params, 2 ) } ) return element( name, { pos, unpack( params, 2 ) } )
end end
end end
local function ElemPosAndDim( pos_units, dim_units, length, is_unlimited ) local function ElemPosAndDim( pos_units, dim_units, length, is_unlimited )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, pos_units, dim_units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], pos_units, dim_units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
0, 0,
0 0
} ) } )
assert( pos_and_dim and ( #params == length or is_unlimited and #params > length ), "Cannot parse formspec element " .. name .. "[]" ) assert( pos_and_dim and ( #params == length or is_unlimited and #params > length ), "Cannot parse formspec element " .. name .. "[]" )
return element( name, { pos_and_dim, unpack( params, 3 ) } ) return element( name, { pos_and_dim, unpack( params, 3 ) } )
end end
end end
local function ElemPosAndDimX( pos_units, dim_units, length, is_unlimited ) local function ElemPosAndDimX( pos_units, dim_units, length, is_unlimited )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim_x = get_pos_and_dim_x( params, pos_units, dim_units, { local pos_and_dim_x = tx.get_pos_and_dim_x( params[ 1 ], params[ 2 ], pos_units, dim_units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
0 0
} ) } )
assert( pos_and_dim_x and ( #params == length or is_unlimited and #params > length ), "Cannot parse formspec element " .. name .. "[]" ) assert( pos_and_dim_x and ( #params == length or is_unlimited and #params > length ), "Cannot parse formspec element " .. name .. "[]" )
return element( name, { pos_and_dim_x, unpack( params, 3 ) } ) return element( name, { pos_and_dim_x, unpack( params, 3 ) } )
end end
end end
------------------------------ ------------------------------
-- special element subclasses -- Special Element Subclasses
------------------------------ ------------------------------
local function SizeElement( ) local function SizeElement( )
local pattern = "^(%d+)([iscp]?),(%d+)([iscpb]?)$" local pattern = "^(%d+)([iscp]?),(%d+)([iscpb]?)$"
local replace = "%0.3f,%0.3f,true" local replace = "%0.3f,%0.3f,true"
return function ( name, params ) return function ( tx, name, params )
local dim, count = string.gsub( params[ 1 ], pattern, function ( dim_x, u1, dim_y, u2 ) local dim, count = string.gsub( params[ 1 ], pattern, function ( dim_x, u1, dim_y, u2 )
return string.format( replace, return string.format( replace,
units.get_x( dim_x, u1 ), tx.units.get_x( dim_x, u1 ),
units.get_y( dim_y, u2 ) tx.units.get_y( dim_y, u2 )
) )
end ) end )
assert( count == 1, "Cannot parse formspec element size[]" ) assert( count == 1, "Cannot parse formspec element size[]" )
return element( "size", { dim } ) return element( "size", { dim } )
end end
end end
local function ListElement( ) -- list[<inventory_location>;<list_name>;<x>,<y>;<colums>;<rows>]
local function ListElement( )
local pattern = "^(%d+)([icp]?),(%d+)([icpb]?)$" local pattern = "^(%d+)([icp]?),(%d+)([icpb]?)$"
local replace = "%0.3f,%0.3f" local replace = "%0.3f,%0.3f"
return function ( name, params ) return function ( tx, name, params )
local pos, count = string.gsub( params[ 3 ], pattern, function ( pos_x, u1, pos_y, u2 ) local pos = tx.get_pos( params[ 3 ], tx.units, {
return string.format( replace, -tx.padding_width,
units.get_x( pos_x, u1 ) - padding_width, -tx.padding_height,
units.get_y( pos_y, u2 ) - padding_height } )
) assert( pos and ( #params == 4 or #params == 5 ), "Cannot parse formspec element list[]" )
end )
assert( count == 1, "Cannot parse formspec element list[]" )
return element( "list", { params[ 1 ], params[ 2 ], pos, unpack( params, 4 ) } ) return element( "list", { params[ 1 ], params[ 2 ], pos, unpack( params, 4 ) } )
end end
end
-- margin[]
-- margin[<x>,<y>]
local function MarginElement( )
local pattern = "^(-?[0-9.]+)([icpd]?),(-?[0-9.]+)([icpdb]?)$"
return function ( tx, name, params )
if #params == 0 then
tx.insert_margin( tx.padding_width, tx.padding_height )
else
local pos_x, u1, pos_y, u2 = string.match( params[ 1 ], pattern )
assert( pos_x and #params == 1, "Cannot parse formspec element margin[]" )
tx.insert_margin( tx.units.get_x( pos_x, u1 ), tx.units.get_y( pos_y, u2 ) )
end end
return "" -- remove virtual element
-- container[<x>,<y>]
local function ContainerElement( )
return function ( name, params )
local pos = get_pos( params, units, { 0, 0 } )
assert( pos and #params == 1, "Cannot parse formspec element container[]" )
return element( "container", { pos } )
end end
end
-- margin_end[]
local function MarginEndElement( )
return function ( tx, name, params )
assert( #params == 0, "Cannot parse formspec element container_end[]" )
tx.remove_margin( )
return "" -- remove virtual element
end end
end
-- background[<w>,<h>;<texture name>] -- background[<w>,<h>;<texture name>]
local function BackgroundElement( ) local function BackgroundElement( )
local pattern = "^(-?%d+),(-?%d+)$" local pattern = "^(-?%d+),(-?%d+)$"
local replace = "%d,%d;0,0" local replace = "%d,%d;0,0"
return function ( name, params ) return function ( tx, name, params )
local dim, count = string.gsub( params[ 1 ], pattern, function ( pos_x, pos_y ) local dim, count = string.gsub( params[ 1 ], pattern, function ( pos_x, pos_y )
return string.format( replace, -pos_x, -pos_y ) return string.format( replace, -pos_x, -pos_y )
end ) end )
assert( count == 1, "Cannot parse formspec element background[]" ) assert( count == 1, "Cannot parse formspec element background[]" )
return element( "background", { dim, params[ 2 ], "true" } ) return element( "background", { dim, params[ 2 ], "true" } )
end end
end end
-- bgimage[<x>,<y>;<w>,<h>;<texture name>] -- bgimage[<x>,<y>;<w>,<h>;<texture name>]
local function BgImageElement( ) local function BgImageElement( )
return function ( tx, name, params )
-- original formula: vx * spacing.x - ( spacing.x - imgsize.x ) / 2 -- original formula: vx * spacing.x - ( spacing.x - imgsize.x ) / 2
local pos_offset_x = 0.5 * 1 / 5 local pos_offset_x = 0.5 * 1 / 5
local pos_offset_y = 0.5 * 2 / 15 local pos_offset_y = 0.5 * 2 / 15
return function ( name, params ) local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
local pos_and_dim = get_pos_and_dim( params, units, units, { -tx.padding_width + pos_offset_x,
-padding_width + pos_offset_x, -tx.padding_height + pos_offset_y,
-padding_height + pos_offset_y,
0, 0,
0 0
} ) } )
assert( pos_and_dim and #params == 3, "Cannot parse formspec element bgimage[]" ) assert( pos_and_dim and #params == 3, "Cannot parse formspec element bgimage[]" )
return element( "background", { pos_and_dim, params[ 3 ], "false" } ) return element( "background", { pos_and_dim, params[ 3 ], "false" } )
end end
end end
-- label[<x>,<y>;<label>] -- label[<x>,<y>;<label>]
-- vertlabel[<x>,<y>;<label>] -- vertlabel[<x>,<y>;<label>]
local function LabelElement( is_vertical ) local function LabelElement( is_vertical )
return function ( tx, name, params )
-- original formula: ( vy + 7 / 30 ) * spacing.y - m_button_height -- original formula: ( vy + 7 / 30 ) * spacing.y - m_button_height
local pos_offset_y = -7 / 30 + button_height / 2 local pos_offset_y = -7 / 30 + tx.button_height / 2
return function ( name, params ) local pos = tx.get_pos( params[ 1 ], tx.units, {
local pos = get_pos( params, units, { -tx.padding_width,
-padding_width, -tx.padding_height + pos_offset_y,
-padding_height + pos_offset_y,
} ) } )
assert( pos and #params == 2, "Cannot parse formspec element " .. name .. "[]" ) assert( pos and #params == 2, "Cannot parse formspec element " .. name .. "[]" )
return element( is_vertical and "vertlabel" or "label", { pos, params[ 2 ] } ) return element( is_vertical and "vertlabel" or "label", { pos, params[ 2 ] } )
end end
end end
-- checkbox[<x>,<y>;<w>;<name>;<label>] -- checkbox[<x>,<y>;<w>;<name>;<label>]
local function CheckboxElement( ) local function CheckboxElement( )
return function ( tx, name, params )
-- original formula: vy * spacing.y + ( imgsize.y / 2 ) - m_button_height -- original formula: vy * spacing.y + ( imgsize.y / 2 ) - m_button_height
local pos_offset_y = -13 / 30 + button_height / 2 local pos_offset_y = -13 / 30 + tx.button_height / 2
return function ( name, params ) local pos = tx.get_pos( params[ 1 ], tx.units, {
local pos = get_pos( params, units, { -tx.padding_width,
-padding_width, -tx.padding_height + pos_offset_y,
-padding_height + pos_offset_y,
} ) } )
assert( pos and #params == 4, "Cannot parse formspec element checkbox[]" ) assert( pos and #params == 4, "Cannot parse formspec element checkbox[]" )
return element( "checkbox", { pos, unpack( params, 3 ) } ) return element( "checkbox", { pos, unpack( params, 3 ) } )
end end
end end
-- button[<x>,<y>;<w>;<name>;<label>] -- button[<x>,<y>;<w>;<name>;<label>]
local function ButtonElement( ) local function ButtonElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, units, units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
cell_margin_width, tx.cell_margin_width,
cell_margin_height, tx.cell_margin_height,
} ) } )
assert( pos_and_dim and #params == 4, "Cannot parse formspec element " .. name .. "[]" ) assert( pos_and_dim and #params == 4, "Cannot parse formspec element " .. name .. "[]" )
return element( "image_" .. name, { pos_and_dim, "", unpack( params, 3 ) } ) return element( "image_" .. name, { pos_and_dim, "", unpack( params, 3 ) } )
end end
end end
-- image_button[<x>,<y>;<w>;<name>;<texture_name>;<label>] -- image_button[<x>,<y>;<w>;<name>;<texture_name>;<label>]
local function ImageButtonElement( ) local function ImageButtonElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, units, units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
cell_margin_width, tx.cell_margin_width,
cell_margin_height, tx.cell_margin_height,
} ) } )
assert( pos_and_dim and ( #params == 5 or #params == 8 ), "Cannot parse formspec element " .. name .. "[]" ) assert( pos_and_dim and ( #params == 5 or #params == 8 ), "Cannot parse formspec element " .. name .. "[]" )
return element( name, { pos_and_dim, params[ 4 ], params[ 3 ], unpack( params, 5 ) } ) return element( name, { pos_and_dim, params[ 4 ], params[ 3 ], unpack( params, 5 ) } )
end end
end end
-- item_image_button[<x>,<y>;<w>,<h>;<item name>;<name>;<label>] -- item_image_button[<x>,<y>;<w>,<h>;<item name>;<name>;<label>]
local function ItemImageButtonElement( ) local function ItemImageButtonElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, units, units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
cell_margin_width, tx.cell_margin_width,
cell_margin_height, tx.cell_margin_height,
} ) } )
assert( pos_and_dim and #params == 5, "Cannot parse formspec element item_image_button[]" ) assert( pos_and_dim and #params == 5, "Cannot parse formspec element item_image_button[]" )
return element( "item_image_button", { pos_and_dim, params[ 4 ], params[ 3 ], params[ 5 ] } ) return element( "item_image_button", { pos_and_dim, params[ 4 ], params[ 3 ], params[ 5 ] } )
end end
end end
-- textlist[<x>,<y>;<w>,<h>;<name>;...] -- textlist[<x>,<y>;<w>,<h>;<name>;...]
local function TextListElement( ) local function TextListElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, units, units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
0, 0,
0, 0,
} ) } )
assert( pos_and_dim and ( #params == 4 or #params == 5 ), "Cannot parse formspec element textlist[]" ) assert( pos_and_dim and ( #params == 4 or #params == 5 ), "Cannot parse formspec element textlist[]" )
return element( "textlist", { pos_and_dim, unpack( params, 3 ) } ) return element( "textlist", { pos_and_dim, unpack( params, 3 ) } )
end end
end end
-- dropdown[<x>,<y>;<w>,<h>;<name>;...] -- dropdown[<x>,<y>;<w>,<h>;<name>;...]
local function DropdownElement( ) local function DropdownElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim_x = get_pos_and_dim_x( params, units, units, { local pos_and_dim_x = tx.get_pos_and_dim_x( params[ 1 ], params[ 2 ], tx.units, tx.units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
0, 0,
0, 0,
} ) } )
assert( pos_and_dim_x and #params == 5, "Cannot parse formspec element dropdown[]" ) assert( pos_and_dim_x and #params == 5, "Cannot parse formspec element dropdown[]" )
return element( "dropdown", { pos_and_dim_x, unpack( params, 3 ) } ) return element( "dropdown", { pos_and_dim_x, unpack( params, 3 ) } )
end end
end end
-- pwdfield[<x>,<y>;<w>;<name>] -- pwdfield[<x>,<y>;<w>;<name>]
local function PwdFieldElement( ) local function PwdFieldElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim_x = get_pos_and_dim_x( params, units, units, { local pos_and_dim_x = tx.get_pos_and_dim_x( params[ 1 ], params[ 2 ], tx.units, tx.units, {
0, 0,
button_height / 2, tx.button_height / 2,
cell_margin_width, tx.cell_margin_width,
} ) } )
assert( pos_and_dim_x, #params == 3, "Cannot parse formspec element pwdfield[]" ) assert( pos_and_dim_x, #params == 3, "Cannot parse formspec element pwdfield[]" )
return element( "pwdfield", { pos_and_dim_x, params[ 3 ], "" } ) return element( "pwdfield", { pos_and_dim_x, params[ 3 ], "" } )
end end
end end
-- field[<x>,<y>;<w>;<name>;<default>] -- field[<x>,<y>;<w>;<name>;<default>]
local function FieldElement( ) local function FieldElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim_x = get_pos_and_dim_x( params, units, units, { local pos_and_dim_x = tx.get_pos_and_dim_x( params[ 1 ], params[ 2 ], tx.units, tx.units, {
0, 0,
button_height / 2, tx.button_height / 2,
cell_margin_width, tx.cell_margin_width,
} ) } )
assert( pos_and_dim_x and #params == 4, "Cannot parse formspec element field[]" ) assert( pos_and_dim_x and #params == 4, "Cannot parse formspec element field[]" )
return element( "field", { pos_and_dim_x, params[ 3 ], "", params[ 4 ] } ) return element( "field", { pos_and_dim_x, params[ 3 ], "", params[ 4 ] } )
end end
end end
-- caption[<x>,<y>;<w>,<h>;<caption>] -- caption[<x>,<y>;<w>,<h>;<caption>]
local function CaptionElement( ) local function CaptionElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, units, units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
0, 0,
-button_height / 2, -tx.button_height / 2,
cell_margin_width, tx.cell_margin_width,
cell_margin_height + button_height / 2 tx.cell_margin_height + tx.button_height / 2
} ) } )
assert( pos_and_dim and #params == 3, "Cannot parse formspec element caption[]" ) assert( pos_and_dim and #params == 3, "Cannot parse formspec element caption[]" )
return element( "textarea", { pos_and_dim, "", params[ 3 ], "" } ) return element( "textarea", { pos_and_dim, "", params[ 3 ], "" } )
end end
end end
-- textarea[<x>,<y>;<w>,<h>;<name>;<default>] -- textarea[<x>,<y>;<w>,<h>;<name>;<default>]
local function TextAreaElement( ) local function TextAreaElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, units, units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
0, 0,
-button_height / 2, -tx.button_height / 2,
cell_margin_width, tx.cell_margin_width,
cell_margin_height + button_height / 2 tx.cell_margin_height + tx.button_height / 2
} ) } )
assert( pos_and_dim and #params == 4, "Cannot parse formspec element textarea[]" ) assert( pos_and_dim and #params == 4, "Cannot parse formspec element textarea[]" )
return element( "textarea", { pos_and_dim, params[ 3 ], "", params[ 4 ] } ) return element( "textarea", { pos_and_dim, params[ 3 ], "", params[ 4 ] } )
end end
end end
-- horz_scrollbar[<x>,<y>;<w>,<h>;<name>;<value>] -- horz_scrollbar[<x>,<y>;<w>,<h>;<name>;<value>]
-- vert_scrollbar[<x>,<y>;<w>,<h>;<name>;<value>] -- vert_scrollbar[<x>,<y>;<w>,<h>;<name>;<value>]
local function ScrollbarElement( orientation ) local function ScrollbarElement( orientation )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, units, units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
0, 0,
0 0
} ) } )
assert( pos_and_dim and #params == 4, "Cannot parse formspec element " .. name .. "[]" ) assert( pos_and_dim and #params == 4, "Cannot parse formspec element " .. name .. "[]" )
return element( "scrollbar", { pos_and_dim, orientation, unpack( params, 3 ) } ) return element( "scrollbar", { pos_and_dim, orientation, unpack( params, 3 ) } )
end end
end end
-- area_tooltip[<x>,<y>;<w>,<h>;<name>;<tooltip_text>] -- area_tooltip[<x>,<y>;<w>,<h>;<name>;<tooltip_text>]
local function AreaTooltipElement( ) local function AreaTooltipElement( )
return function ( name, params ) return function ( tx, name, params )
local pos_and_dim = get_pos_and_dim( params, units, units, { local pos_and_dim = tx.get_pos_and_dim( params[ 1 ], params[ 2 ], tx.units, tx.units, {
-padding_width, -tx.padding_width,
-padding_height, -tx.padding_height,
0, 0,
0 0
} ) } )
assert( pos_and_dim and #params >= 3, "Cannot parse formspec element area_tooltip[]" ) assert( pos_and_dim and #params >= 3, "Cannot parse formspec element area_tooltip[]" )
return element( "tooltip", { pos_and_dim, unpack( params, 3 ) } ) return element( "tooltip", { pos_and_dim, unpack( params, 3 ) } )
end end
end end
-- tabheader[<x>,<y>;<w>,<h>;<name>;<tooltip_text>] -- tabheader[<x>,<y>;<w>,<h>;<name>;<tooltip_text>]
local function TabHeaderElement( ) local function TabHeaderElement( )
return function ( name, params ) return function ( tx, name, params )
local pos = get_pos( params, units, { local pos = tx.get_pos( params[ 1 ], tx.units, {
0, 0,
0 0
} ) } )
assert( pos and ( #params == 4 or #params == 6 ), "Cannot parse formspec element tabheader[]" ) assert( pos and ( #params == 4 or #params == 6 ), "Cannot parse formspec element tabheader[]" )
return element( "tabheader", { pos, unpack( params, 2 ) } ) return element( "tabheader", { pos, unpack( params, 2 ) } )
end end
end end
------------------- -------------------------
-- element parsers -- Translation Interface
------------------- -------------------------
scarlet.translate = function ( fs, screen_dpi, gui_scaling )
local tx = RuntimeTranslator( screen_dpi, gui_scaling, false )
local element_parsers = { local element_parsers = {
size = SizeElement( ), size = SizeElement( ),
list = ListElement( ), list = ListElement( ),
background = BackgroundElement( ), background = BackgroundElement( ),
box = ElemPosAndDim( units, units, 3, false ), box = ElemPosAndDim( tx.units, tx.units, 3, false ),
button = ButtonElement( ), button = ButtonElement( ),
button_exit = ButtonElement( ), button_exit = ButtonElement( ),
image_button = ImageButtonElement( ), image_button = ImageButtonElement( ),
@ -483,45 +537,39 @@ ScarletDef = function ( screen_dpi, gui_scaling, has_padding )
checkbox = CheckboxElement( ), checkbox = CheckboxElement( ),
pwdfield = PwdFieldElement( ), pwdfield = PwdFieldElement( ),
item_image_button = ItemImageButtonElement( ), item_image_button = ItemImageButtonElement( ),
image = ElemPosAndDim( units, iunits, 3, false ), image = ElemPosAndDim( tx.units, tx.iunits, 3, false ),
item_image = ElemPosAndDim( units, iunits, 3, false ), item_image = ElemPosAndDim( tx.units, tx.iunits, 3, false ),
field = FieldElement( ), field = FieldElement( ),
dropdown = DropdownElement( ), dropdown = DropdownElement( ),
textlist = TextListElement( ), textlist = TextListElement( ),
vert_scrollbar = ScrollbarElement( "vertical" ), vert_scrollbar = ScrollbarElement( "vertical" ),
horz_scrollbar = ScrollbarElement( "horizontal" ), horz_scrollbar = ScrollbarElement( "horizontal" ),
table = ElemPosAndDim( units, units, 5, false ), table = ElemPosAndDim( tx.units, tx.units, 5, false ),
textarea = TextAreaElement( ), textarea = TextAreaElement( ),
caption = CaptionElement( ), caption = CaptionElement( ),
area_tooltip = AreaTooltipElement( ), -- added in 5.0 (https://github.com/minetest/minetest/pull/7469) area_tooltip = AreaTooltipElement( ), -- not added until 5.0 (https://github.com/minetest/minetest/pull/7469)
container = ContainerElement( ), -- fixed in 5.0 (https://github.com/minetest/minetest/pull/7497) container = ElemPos( tx.units, 1 ), -- not fixed until 5.0 (https://github.com/minetest/minetest/pull/7497)
margin = MarginElement( ), -- emulation of container[] element
margin_end = MarginEndElement( ),
tabheader = TabHeaderElement( ), tabheader = TabHeaderElement( ),
} }
-------------------------
-- translation interface
-------------------------
local function translate( fs )
fs = string.gsub( fs, "([a-z_]+)%[(.-)%]", function( name, parts ) fs = string.gsub( fs, "([a-z_]+)%[(.-)%]", function( name, parts )
local parser = element_parsers[ name ] local parser = element_parsers[ name ]
if parser then if parser then
local res = parser( name, string.split( parts, ";", true ) ) local res = parser( tx, name, parts == "" and { } or string.split( parts, ";", true ) )
return res return res
end end
end ) end )
minetest.debug( "ACTION", "Result:" .. fs ) -- minetest.debug( "ACTION", "Result:" .. fs )
return fs return fs
end
return {
-- public method
translate = translate
}
end end
scarlet = { scarlet.translate_96dpi = function ( fs )
translate_96dpi = ScarletDef( 96, 1, false ).translate, return scarlet.translate( fs, 96, 1 )
translate_72dpi = ScarletDef( 72, 1, false ).translate end
}
scarlet.translate_72dpi = function ( fs )
return scarlet.translate( fs, 72, 1 )
end