mNo edit summary |
mNo edit summary |
||
Line 3: | Line 3: | ||
local p = {} | local p = {} | ||
local ProtofluxColor = require("Module:ProtoFlux_Type_Color") | |||
local | |||
{ | local css = { | ||
root = function(self, rows) | |||
return string.format("color: rgb(224, 224, 224); background-color: rgb(18, 20, 28); width: 256px; display: grid; grid-template-columns: [input] 30px [label] 1fr [output] 30px [end]; grid-template-rows: 80px repeat(%s, 35px 35px) 60px;", | |||
rows) | |||
end, | |||
node_title = function(self, minimal, rows) | |||
if minimal then | |||
return string.format("text-align:center; font-weight: bold; font-size: 18pt; grid-column: label; grid-row: 1/%s; align-self: center;", | |||
(rows * 2) + 3) | |||
else | |||
return "padding: 10px 0px 10px 0px; text-align:center; font-weight: bold; background-color: rgb(26, 41, 54); font-size: 18pt; grid-column: input / end; grid-row: 1;" | |||
end | |||
end, | |||
node_footer = "text-align: center; padding: 10px; font-size: 18pt; color: rgb(64,64,64); font-weight: bold;", | |||
connector_row = "display: flex; min-height: 70px;", | |||
connector_row_labels = "flex-grow: 2; overflow: visible; display: flex; flex-direction: column; justify-content: space-between; order: 2;", | |||
multi_button = "font-size: 1.75rem; width: 1em; height: 1em; background-color: #2c2f35; border-radius: 2em; " | |||
.. "display:flex; align-items:center; justify-content: center;", | |||
multi_bar = function(self, is_input, multi) | |||
return string.format( | |||
"background-color: white; opacity: 0.5; width: 6px; height: %spx; position: absolute; bottom: 40px; %s: 13px;", | |||
70 * (multi - 2) + 50, | |||
is_input and "left" or "right" | |||
) | |||
end, | |||
label = "text-overflow: ellipsis; border-bottom: 4px solid #11151d; border-top: 4px solid #11151d; ", | |||
label_multi = "font-weight: bold; font-size: 1.4rem; line-height: 1;", | |||
input_label = function(self, color, is_multi) | |||
if is_multi then color = nil end | |||
return string.format( | |||
self.label | |||
.."text-align: left; overflow: %s; padding-left: 4px; border-right: 20px solid #11151d; " | |||
.."background-color: %s;%s", | |||
color and "hidden" or "visible", | |||
color and ColorToCss(MultA(color, 0.6)) or "transparent", | |||
is_multi and " "..self.label_multi or "" | |||
) | |||
end, | |||
output_label = function(self, color, is_multi) | |||
if is_multi then color = nil end | |||
return string.format( | |||
self.label | |||
.."text-align: right; overflow: %s; padding-right: 4px; border-left: 20px solid #11151d; " | |||
.."background-color: %s;%s", | |||
color and "hidden" or "visible", | |||
color and ColorToCss(MultA(color, 0.6)) or "transparent", | |||
is_multi and " "..self.label_multi or "" | |||
) | |||
end, | |||
attachment_point = function(self, color, order) | |||
return string.format( | |||
"width: 30px; background-color: black; fill: %s; stroke: %s; order: %s;", | |||
ColorToCss(MultA(color, 0.15)), | |||
ColorToCss(color), | |||
order | |||
) | |||
end, | |||
global = function(self, color) | |||
return string.format( | |||
"display: flex; min-height: 70px; flex-direction: column; border-left: 10px solid %s;", | |||
ColorToCss(color) | |||
) | |||
end, | |||
global_input = "border-radius: 16px; text-align: center; display: flex; flex-direction: column; justify-content: center;" | |||
} | } | ||
Line 33: | Line 78: | ||
local outputs = mw.text.jsonDecode(frame.args.Outputs or '[]') | local outputs = mw.text.jsonDecode(frame.args.Outputs or '[]') | ||
local globals = mw.text.jsonDecode(frame.args.Globals or '[]') | local globals = mw.text.jsonDecode(frame.args.Globals or '[]') | ||
local | |||
-- Create an HTML div element to contain our node UI | |||
local protofluxContainer = mw.html.create( 'div' ) | |||
protofluxContainer | |||
:attr('class', frame.args.Inline and '' or 'floatright') | |||
:cssText(css.root(maxRows)) | |||
:tag('div') -- HTML div to contain the node title | |||
:cssText(css.node_title(mimimal, maxRows)) | |||
:wikitext(frame.args.Name) | |||
:done() -- Close node title div | |||
local processedInputs = {} | local processedInputs = {} | ||
local processedOutputs = {} | local processedOutputs = {} | ||
for i=1,#inputs do | for i=1,#inputs do | ||
local multi = tonumber(inputs[i] and inputs[i].Multi or 1) | |||
for j=1,multi do | |||
inputs[i].MultiIndex = j | inputs[i].MultiIndex = j | ||
table.insert(processedInputs, { Name=inputs[i].Name, Type=inputs[i].Type, Multi= | table.insert(processedInputs, { Name=inputs[i].Name, Type=inputs[i].Type, Multi=multi, MultiIndex=j }) | ||
end | end | ||
end | end | ||
for i=1,#outputs do | for i=1,#outputs do | ||
local multi = tonumber(outputs[i] and outputs[i].Multi or 1) | |||
for j=1,multi do | |||
table.insert(processedOutputs, { Name=outputs[i].Name, Type=outputs[i].Type, Multi= | table.insert(processedOutputs, { Name=outputs[i].Name, Type=outputs[i].Type, Multi=multi, MultiIndex=j }) | ||
end | end | ||
end | end | ||
Line 54: | Line 110: | ||
local maxRows = math.max(#processedInputs, #processedOutputs) | local maxRows = math.max(#processedInputs, #processedOutputs) | ||
-- Iterate over each row, and populate it with the inputs/outputs. If the node is asymmetric, | -- Iterate over each row, and populate it with the inputs/outputs. If the node is asymmetric, | ||
-- the value passed for either input or output might be nil. | -- the value passed for either input or output might be nil. | ||
for i=1,maxRows do | for i=1,maxRows do | ||
CreateConnectorRow( | CreateConnectorRow(protofluxContainer, processedInputs[i], processedOutputs[i]) | ||
end | end | ||
Line 82: | Line 121: | ||
-- of the node, and so don't need to be balanced in any way. | -- of the node, and so don't need to be balanced in any way. | ||
for i=1,#globals do | for i=1,#globals do | ||
CreateGlobalsRow(protofluxContainer, globals[i]) | |||
end | end | ||
protofluxContainer | protofluxContainer | ||
:tag('div') -- HTML div to contain the node category footer | :tag('div') -- HTML div to contain the node category footer | ||
:cssText( | :cssText(css.node_footer) | ||
:wikitext(frame.args.Category) | :wikitext(frame.args.Category) | ||
:done() -- Close category footer div | :done() -- Close category footer div | ||
Line 93: | Line 132: | ||
-- Return the HTML generated above to the wiki page this script is invoked from. | -- Return the HTML generated above to the wiki page this script is invoked from. | ||
return tostring(protofluxContainer) .. '[[Category:ProtoFlux:All]]' | return tostring(protofluxContainer) .. '[[Category:ProtoFlux:All]]' | ||
end | |||
--- Creates a text which should be shown when hovering over a connector | |||
-- @param Input Table containing a Name (optional) and Type for the input on this row. | |||
function connector_hover_text(connector) | |||
if connector.Name == nil then | |||
return ("<%s>"):format(connector.Type) | |||
end | |||
return ("%s <%s>"):format(connector.Name, connector.Type) | |||
end | end | ||
--- Creates a new ProtoFlux connector row in the output | --- Creates a new ProtoFlux connector row in the output | ||
-- @param | -- @param container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort. | ||
-- @param | -- @param input Table containing a Name and Type for the input on this row. Can be nil if no input is to be placed on this row. | ||
-- @param | -- @param output Table containing a Name and Type for the output on this row. Can be nil if no output is to be placed on this row. | ||
function CreateConnectorRow( | function CreateConnectorRow(container, input, output) | ||
local connector_row = container | |||
:tag('div') -- HTML div to contain the connector row. We specify a min-height of 70px for consistency. | |||
:cssText(css.connector_row) | |||
local connector_row_labels = connector_row | |||
:tag('div') -- HTML div to contain the input and output labels in the connector row | |||
:cssText(css.connector_row_labels) | |||
if input then | |||
local color, is_impulse = GetColor(input) | |||
local hover_text = connector_hover_text(input) | |||
if input.Multi and input.MultiIndex > 1 then | |||
local label = connector_row_labels | |||
if | |||
:tag('div') -- HTML div to contain the input label | :tag('div') -- HTML div to contain the input label | ||
:attr('title', | :attr('title', hover_text) -- Add a basic mouseover description | ||
:cssText( | :cssText(css:input_label()) | ||
if input.Multi == input.MultiIndex then | |||
label | |||
:tag('div') | :tag('div') | ||
:cssText(' | :cssText('position:relative; top:2rem; display: flex; gap: 4px;') | ||
:tag('div') | |||
: | :cssText(css.multi_button) | ||
:wikitext('+') | |||
:done() | |||
:tag('div') | |||
:cssText(css.multi_button) | |||
:wikitext('-') | |||
:done() | |||
:tag('div') | |||
:cssText(css:multi_bar(true, input.Multi)) | |||
end | |||
else | else | ||
connector_row_labels | |||
:tag('div') -- HTML div to contain the input label | :tag('div') -- HTML div to contain the input label | ||
:attr('title', | :attr('title', hover_text) -- Add a basic mouseover description | ||
:cssText( | :cssText(css:input_label(color, input.Multi > 1)) | ||
:wikitext( | :wikitext(input.Name or '') | ||
:done() -- Close input label div | :done() -- Close input label div | ||
end | end | ||
-- Create the input (left) attachement point | |||
local segments = GetTypeSegments(input) | |||
CreateConnectorAttachmentPoint(connector_row, hover_text, color, segments, is_impulse, true) | |||
else -- if input | |||
connector_row:tag("div"):cssText("width: 30px; order: 1;") | |||
connector_row_labels:tag("div") | |||
end -- if input | |||
if output then | |||
local color, is_impulse = GetColor(output) | |||
local hover_text = connector_hover_text(output) | |||
if output.Multi and output.MultiIndex > 1 then | |||
local label = connector_row_labels | |||
:tag('div') -- HTML div to contain the output label | :tag('div') -- HTML div to contain the output label | ||
:attr('title', | :attr('title', hover_text) -- Add a basic mouseover description | ||
:cssText( | :cssText(css:output_label()) | ||
if output.Multi == output.MultiIndex then | |||
label | |||
:tag('div') | :tag('div') | ||
:cssText(' | :cssText('position:relative; display: flex; justify-content: flex-end; gap: 4px;') | ||
:wikitext('+') | :tag('div') | ||
:cssText(css.multi_button) | |||
:wikitext('+') | |||
:done() | |||
: | :tag('div') | ||
:cssText(css.multi_button) | |||
:wikitext('-') | |||
:done() | |||
:tag('div') | |||
:cssText(css:multi_bar(false, output.Multi)) | |||
end | |||
else | else | ||
connector_row_labels | |||
:tag('div') -- HTML div to contain the output label | |||
:attr('title', hover_text) -- Add a basic mouseover description | |||
:cssText(css:output_label(color, output.Multi > 1)) | |||
:wikitext(output.Name or '') | |||
:done() -- Close output label div | |||
end | end | ||
-- Create the output (right) attachment point | |||
local segments = GetTypeSegments(output) | |||
CreateConnectorAttachmentPoint(connector_row, hover_text, color, segments, is_impulse, false); | |||
else -- if output | |||
connector_row:tag("div"):cssText("width: 30px; order: 3;") | |||
end -- if output | |||
end | end | ||
--- Creates a new ProtoFlux global input field row in the output | --- Creates a new ProtoFlux global input field row in the output | ||
-- @param | -- @param container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort. | ||
-- @param | -- @param global Table containing a Name and Type for the global field on this row. | ||
function CreateGlobalsRow( | function CreateGlobalsRow(container, global) | ||
local color = GetTypeColor(global) | |||
local hover_text = connector_hover_text(global) | |||
container | |||
:tag( 'div' ) | :tag( 'div' ) | ||
:attr('title', | :attr('title', hover_text) -- Add a basic mouseover description | ||
:cssText( | :cssText(css:global(color)) | ||
:tag( 'div' ) | :tag( 'div' ) | ||
:cssText('display: flex; flex-direction: row; align-items: center; flex-grow:1; overflow: hidden;') | :cssText('display: flex; flex-direction: row; align-items: center; flex-grow:1; overflow: hidden;') | ||
:tag( 'span' ) | :tag( 'span' ) | ||
:cssText('text-align: center; overflow: hidden; text-overflow: ellipsis; font-size: 14pt; font-weight: bold; flex-grow:1;') | :cssText('text-align: center; overflow: hidden; text-overflow: ellipsis; font-size: 14pt; font-weight: bold; flex-grow:1;') | ||
:wikitext( | :wikitext(global.Name) | ||
:done() | :done() | ||
:done() | :done() | ||
:tag( 'div' ) | :tag( 'div' ) | ||
:attr('style', 'display:flex; gap: 10px; flex-grow: 1;') | :attr('style', 'display: flex; gap: 10px; flex-grow: 1;') | ||
:tag('div') | :tag('div') | ||
:cssText(' | :cssText('background-color: #777; font-style: italic; flex-grow: 3; '..css.global_input) | ||
:tag( 'span' ) | :tag( 'span' ) | ||
:wikitext('null') | :wikitext('null') | ||
:done() | :done() | ||
:done() | :done() | ||
:tag( 'div') | :tag( 'div' ) | ||
:cssText(' | :cssText('background-color: #333; flex-grow: 1; '..css.global_input) | ||
:tag ( 'span' ) | :tag ( 'span' ) | ||
:wikitext('∅') | :wikitext('∅') | ||
Line 200: | Line 271: | ||
--- Creates a new input or output attachement point for a connector row. | --- Creates a new input or output attachement point for a connector row. | ||
-- @param | -- @param container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort. | ||
-- @param | -- @param hover_text Text to show when hovering, possibly nil | ||
-- @param | -- @param color The color of this attachment point (table with r, g, b, optionally a). | ||
function CreateConnectorAttachmentPoint( | -- @param segments The number of segments for the type, used for vectors or matrices. | ||
if | -- @param is_impulse True if this is an impulse, false if it is a data type. | ||
-- @param is_input True if this is an input side attachment point, false if it is an output side attachment point. | |||
function CreateConnectorAttachmentPoint(container, hover_text, color, segments, is_impulse, is_input) | |||
local connector_type = nil | |||
if is_impulse then connector_type = "InputArrow" | |||
elseif is_input then connector_type = "InputBox" | |||
else connector_type = "OutputBox" | |||
end | |||
container | |||
:tag( 'div' ) | |||
:attr('title', hover_text or "") -- Add a basic mouseover description | |||
:cssText(css:attachment_point(color, is_input and 1 or 3)) | |||
:wikitext(mw.getCurrentFrame():expandTemplate({title = 'ProtoFluxConnector', args = {Type=connector_type, Segments=segments}})) | |||
end | |||
--- Returns a CSS color for the data type or impulse and whether the type is an impulse | |||
-- @param connector Table containing a Name (Optional) and Type (Required) for the input/output. | |||
function GetColor(connector) | |||
if connector == nil then error("connector is nil") end | |||
if connector == nil or connector.Type == nil then error("Missing Type") end | |||
local color = ProtofluxColor.get_impulse_color(connector.Type) | |||
if color ~= nil then | |||
return color, true | |||
end | end | ||
return GetTypeColor(connector), false | |||
end | |||
--- Returns a CSS color for the data type | |||
-- @param connector Table containing a Name (Optional) and Type (Required) for the input/output or global. | |||
function GetTypeColor(connector) | |||
if connector == nil then error("connector is nil") end | |||
if connector == nil or connector.Type == nil then error("Missing Type") end | |||
local color = ProtofluxColor.get_type_color(connector.Type) | |||
return MultRGB(color, 1.5) | |||
end | |||
function MultRGB(color, fact) | |||
return { | |||
r=color.r*fact, | |||
g=color.g*fact, | |||
b=color.b*fact, | |||
a=color.a, | |||
} | |||
end | |||
function MultA(color, fact) | |||
return { | |||
r=color.r, | |||
g=color.g, | |||
b=color.b, | |||
a=(color.a or 1)*fact, | |||
} | |||
end | |||
function ColorToCss(color) | |||
return ("rgba(%s,%s,%s,%s)"):format(color.r*255, color.g*255, color.b*255, color.a or 1) | |||
end | end | ||
--- Returns | local type_segments = { | ||
-- @param | enabled = { | ||
bool=true, | |||
function | byte=true, | ||
sbyte=true, | |||
ushort=true, | |||
short=true, | |||
uint=true, | |||
int=true, | |||
ulong=true, | |||
long=true, | |||
float=true, | |||
double=true, | |||
decimal=true, | |||
}, | |||
suffix = { | |||
["2"]=2, | |||
["3"]=3, | |||
["4"]=4, | |||
--["2x2"]=2, | |||
--["3x3"]=3, | |||
--["4x4"]=4, | |||
}, | |||
} | |||
--- Returns a CSS color for the data type | |||
-- @param connector Table containing a Name (Optional) and Type (Required) for the input/output. | |||
function GetTypeSegments(connector) | |||
if connector == nil then error("connector is nil") end | |||
if connector == nil or connector.Type == nil then error("Missing Type") end | |||
local base = connector.Type:match("^%a+") | |||
if not type_segments.enabled[base] then return 1 end | |||
local suffix = connector.Type:sub(#base+1) | |||
return type_segments.suffix[suffix] or 1 | |||
end | end | ||
return p | return p |
Revision as of 17:05, 24 February 2024
Documentation for this module may be created at Module:Test/doc
-- Package definition to return to Scribunto - this allows us to define methods that can be called
-- when this module is targeted.
local p = {}
local ProtofluxColor = require("Module:ProtoFlux_Type_Color")
local css = {
root = function(self, rows)
return string.format("color: rgb(224, 224, 224); background-color: rgb(18, 20, 28); width: 256px; display: grid; grid-template-columns: [input] 30px [label] 1fr [output] 30px [end]; grid-template-rows: 80px repeat(%s, 35px 35px) 60px;",
rows)
end,
node_title = function(self, minimal, rows)
if minimal then
return string.format("text-align:center; font-weight: bold; font-size: 18pt; grid-column: label; grid-row: 1/%s; align-self: center;",
(rows * 2) + 3)
else
return "padding: 10px 0px 10px 0px; text-align:center; font-weight: bold; background-color: rgb(26, 41, 54); font-size: 18pt; grid-column: input / end; grid-row: 1;"
end
end,
node_footer = "text-align: center; padding: 10px; font-size: 18pt; color: rgb(64,64,64); font-weight: bold;",
connector_row = "display: flex; min-height: 70px;",
connector_row_labels = "flex-grow: 2; overflow: visible; display: flex; flex-direction: column; justify-content: space-between; order: 2;",
multi_button = "font-size: 1.75rem; width: 1em; height: 1em; background-color: #2c2f35; border-radius: 2em; "
.. "display:flex; align-items:center; justify-content: center;",
multi_bar = function(self, is_input, multi)
return string.format(
"background-color: white; opacity: 0.5; width: 6px; height: %spx; position: absolute; bottom: 40px; %s: 13px;",
70 * (multi - 2) + 50,
is_input and "left" or "right"
)
end,
label = "text-overflow: ellipsis; border-bottom: 4px solid #11151d; border-top: 4px solid #11151d; ",
label_multi = "font-weight: bold; font-size: 1.4rem; line-height: 1;",
input_label = function(self, color, is_multi)
if is_multi then color = nil end
return string.format(
self.label
.."text-align: left; overflow: %s; padding-left: 4px; border-right: 20px solid #11151d; "
.."background-color: %s;%s",
color and "hidden" or "visible",
color and ColorToCss(MultA(color, 0.6)) or "transparent",
is_multi and " "..self.label_multi or ""
)
end,
output_label = function(self, color, is_multi)
if is_multi then color = nil end
return string.format(
self.label
.."text-align: right; overflow: %s; padding-right: 4px; border-left: 20px solid #11151d; "
.."background-color: %s;%s",
color and "hidden" or "visible",
color and ColorToCss(MultA(color, 0.6)) or "transparent",
is_multi and " "..self.label_multi or ""
)
end,
attachment_point = function(self, color, order)
return string.format(
"width: 30px; background-color: black; fill: %s; stroke: %s; order: %s;",
ColorToCss(MultA(color, 0.15)),
ColorToCss(color),
order
)
end,
global = function(self, color)
return string.format(
"display: flex; min-height: 70px; flex-direction: column; border-left: 10px solid %s;",
ColorToCss(color)
)
end,
global_input = "border-radius: 16px; text-align: center; display: flex; flex-direction: column; justify-content: center;"
}
--- Method that generates the HTML output
-- @param frame A Scribunto frame instance. frame.args contains the parameters passed into the module call
function p.GenerateUI( frame )
-- Parse the JSON input, returning an empty array if no argument was passed
local inputs = mw.text.jsonDecode(frame.args.Inputs or '[]')
local outputs = mw.text.jsonDecode(frame.args.Outputs or '[]')
local globals = mw.text.jsonDecode(frame.args.Globals or '[]')
-- Create an HTML div element to contain our node UI
local protofluxContainer = mw.html.create( 'div' )
protofluxContainer
:attr('class', frame.args.Inline and '' or 'floatright')
:cssText(css.root(maxRows))
:tag('div') -- HTML div to contain the node title
:cssText(css.node_title(mimimal, maxRows))
:wikitext(frame.args.Name)
:done() -- Close node title div
local processedInputs = {}
local processedOutputs = {}
for i=1,#inputs do
local multi = tonumber(inputs[i] and inputs[i].Multi or 1)
for j=1,multi do
inputs[i].MultiIndex = j
table.insert(processedInputs, { Name=inputs[i].Name, Type=inputs[i].Type, Multi=multi, MultiIndex=j })
end
end
for i=1,#outputs do
local multi = tonumber(outputs[i] and outputs[i].Multi or 1)
for j=1,multi do
table.insert(processedOutputs, { Name=outputs[i].Name, Type=outputs[i].Type, Multi=multi, MultiIndex=j })
end
end
-- Calculate the larger number of rows required (either for inputs or outputs, if the node is asymmetric)
local maxRows = math.max(#processedInputs, #processedOutputs)
-- Iterate over each row, and populate it with the inputs/outputs. If the node is asymmetric,
-- the value passed for either input or output might be nil.
for i=1,maxRows do
CreateConnectorRow(protofluxContainer, processedInputs[i], processedOutputs[i])
end
-- Iterate over each global value in the node, and create a row. These elements take up the entire width
-- of the node, and so don't need to be balanced in any way.
for i=1,#globals do
CreateGlobalsRow(protofluxContainer, globals[i])
end
protofluxContainer
:tag('div') -- HTML div to contain the node category footer
:cssText(css.node_footer)
:wikitext(frame.args.Category)
:done() -- Close category footer div
-- Return the HTML generated above to the wiki page this script is invoked from.
return tostring(protofluxContainer) .. '[[Category:ProtoFlux:All]]'
end
--- Creates a text which should be shown when hovering over a connector
-- @param Input Table containing a Name (optional) and Type for the input on this row.
function connector_hover_text(connector)
if connector.Name == nil then
return ("<%s>"):format(connector.Type)
end
return ("%s <%s>"):format(connector.Name, connector.Type)
end
--- Creates a new ProtoFlux connector row in the output
-- @param container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort.
-- @param input Table containing a Name and Type for the input on this row. Can be nil if no input is to be placed on this row.
-- @param output Table containing a Name and Type for the output on this row. Can be nil if no output is to be placed on this row.
function CreateConnectorRow(container, input, output)
local connector_row = container
:tag('div') -- HTML div to contain the connector row. We specify a min-height of 70px for consistency.
:cssText(css.connector_row)
local connector_row_labels = connector_row
:tag('div') -- HTML div to contain the input and output labels in the connector row
:cssText(css.connector_row_labels)
if input then
local color, is_impulse = GetColor(input)
local hover_text = connector_hover_text(input)
if input.Multi and input.MultiIndex > 1 then
local label = connector_row_labels
:tag('div') -- HTML div to contain the input label
:attr('title', hover_text) -- Add a basic mouseover description
:cssText(css:input_label())
if input.Multi == input.MultiIndex then
label
:tag('div')
:cssText('position:relative; top:2rem; display: flex; gap: 4px;')
:tag('div')
:cssText(css.multi_button)
:wikitext('+')
:done()
:tag('div')
:cssText(css.multi_button)
:wikitext('-')
:done()
:tag('div')
:cssText(css:multi_bar(true, input.Multi))
end
else
connector_row_labels
:tag('div') -- HTML div to contain the input label
:attr('title', hover_text) -- Add a basic mouseover description
:cssText(css:input_label(color, input.Multi > 1))
:wikitext(input.Name or '')
:done() -- Close input label div
end
-- Create the input (left) attachement point
local segments = GetTypeSegments(input)
CreateConnectorAttachmentPoint(connector_row, hover_text, color, segments, is_impulse, true)
else -- if input
connector_row:tag("div"):cssText("width: 30px; order: 1;")
connector_row_labels:tag("div")
end -- if input
if output then
local color, is_impulse = GetColor(output)
local hover_text = connector_hover_text(output)
if output.Multi and output.MultiIndex > 1 then
local label = connector_row_labels
:tag('div') -- HTML div to contain the output label
:attr('title', hover_text) -- Add a basic mouseover description
:cssText(css:output_label())
if output.Multi == output.MultiIndex then
label
:tag('div')
:cssText('position:relative; display: flex; justify-content: flex-end; gap: 4px;')
:tag('div')
:cssText(css.multi_button)
:wikitext('+')
:done()
:tag('div')
:cssText(css.multi_button)
:wikitext('-')
:done()
:tag('div')
:cssText(css:multi_bar(false, output.Multi))
end
else
connector_row_labels
:tag('div') -- HTML div to contain the output label
:attr('title', hover_text) -- Add a basic mouseover description
:cssText(css:output_label(color, output.Multi > 1))
:wikitext(output.Name or '')
:done() -- Close output label div
end
-- Create the output (right) attachment point
local segments = GetTypeSegments(output)
CreateConnectorAttachmentPoint(connector_row, hover_text, color, segments, is_impulse, false);
else -- if output
connector_row:tag("div"):cssText("width: 30px; order: 3;")
end -- if output
end
--- Creates a new ProtoFlux global input field row in the output
-- @param container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort.
-- @param global Table containing a Name and Type for the global field on this row.
function CreateGlobalsRow(container, global)
local color = GetTypeColor(global)
local hover_text = connector_hover_text(global)
container
:tag( 'div' )
:attr('title', hover_text) -- Add a basic mouseover description
:cssText(css:global(color))
:tag( 'div' )
:cssText('display: flex; flex-direction: row; align-items: center; flex-grow:1; overflow: hidden;')
:tag( 'span' )
:cssText('text-align: center; overflow: hidden; text-overflow: ellipsis; font-size: 14pt; font-weight: bold; flex-grow:1;')
:wikitext(global.Name)
:done()
:done()
:tag( 'div' )
:attr('style', 'display: flex; gap: 10px; flex-grow: 1;')
:tag('div')
:cssText('background-color: #777; font-style: italic; flex-grow: 3; '..css.global_input)
:tag( 'span' )
:wikitext('null')
:done()
:done()
:tag( 'div' )
:cssText('background-color: #333; flex-grow: 1; '..css.global_input)
:tag ( 'span' )
:wikitext('∅')
end
--- Creates a new input or output attachement point for a connector row.
-- @param container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort.
-- @param hover_text Text to show when hovering, possibly nil
-- @param color The color of this attachment point (table with r, g, b, optionally a).
-- @param segments The number of segments for the type, used for vectors or matrices.
-- @param is_impulse True if this is an impulse, false if it is a data type.
-- @param is_input True if this is an input side attachment point, false if it is an output side attachment point.
function CreateConnectorAttachmentPoint(container, hover_text, color, segments, is_impulse, is_input)
local connector_type = nil
if is_impulse then connector_type = "InputArrow"
elseif is_input then connector_type = "InputBox"
else connector_type = "OutputBox"
end
container
:tag( 'div' )
:attr('title', hover_text or "") -- Add a basic mouseover description
:cssText(css:attachment_point(color, is_input and 1 or 3))
:wikitext(mw.getCurrentFrame():expandTemplate({title = 'ProtoFluxConnector', args = {Type=connector_type, Segments=segments}}))
end
--- Returns a CSS color for the data type or impulse and whether the type is an impulse
-- @param connector Table containing a Name (Optional) and Type (Required) for the input/output.
function GetColor(connector)
if connector == nil then error("connector is nil") end
if connector == nil or connector.Type == nil then error("Missing Type") end
local color = ProtofluxColor.get_impulse_color(connector.Type)
if color ~= nil then
return color, true
end
return GetTypeColor(connector), false
end
--- Returns a CSS color for the data type
-- @param connector Table containing a Name (Optional) and Type (Required) for the input/output or global.
function GetTypeColor(connector)
if connector == nil then error("connector is nil") end
if connector == nil or connector.Type == nil then error("Missing Type") end
local color = ProtofluxColor.get_type_color(connector.Type)
return MultRGB(color, 1.5)
end
function MultRGB(color, fact)
return {
r=color.r*fact,
g=color.g*fact,
b=color.b*fact,
a=color.a,
}
end
function MultA(color, fact)
return {
r=color.r,
g=color.g,
b=color.b,
a=(color.a or 1)*fact,
}
end
function ColorToCss(color)
return ("rgba(%s,%s,%s,%s)"):format(color.r*255, color.g*255, color.b*255, color.a or 1)
end
local type_segments = {
enabled = {
bool=true,
byte=true,
sbyte=true,
ushort=true,
short=true,
uint=true,
int=true,
ulong=true,
long=true,
float=true,
double=true,
decimal=true,
},
suffix = {
["2"]=2,
["3"]=3,
["4"]=4,
--["2x2"]=2,
--["3x3"]=3,
--["4x4"]=4,
},
}
--- Returns a CSS color for the data type
-- @param connector Table containing a Name (Optional) and Type (Required) for the input/output.
function GetTypeSegments(connector)
if connector == nil then error("connector is nil") end
if connector == nil or connector.Type == nil then error("Missing Type") end
local base = connector.Type:match("^%a+")
if not type_segments.enabled[base] then return 1 end
local suffix = connector.Type:sub(#base+1)
return type_segments.suffix[suffix] or 1
end
return p