Got fed up of how painful/unintuitive/finicky Derma can be so started working on my own framework to XML-ify it, similar to HTML.
Attached video comes from the following XML markup:
<SymPanel Ref="Top" :X="Parent.Width * 0.25" :Y="Parent.Height * 0.25" :Width="Parent.Width * 0.5" :Height="Parent.Height * 0.5" Flex="4" :Background="{
HTMLCircleGradient(Width, Height, Color(14, 17, 27, 245), 0, Color(0, 0, 0, 245), 50, Color(49, 56, 85, 225), 100),
HTMLRepeatingLinearGradient(Width, Height, 0, color_transparent, '5px', Color(0, 0, 0, 64), '10px', color_transparent, '15px'),
}">
<SymPanel Ref="Sidebar"
:On:ChangeValue="function (p, ply)
self.Value = ply
XVGUI.Content:InvalidateLayout()
return true
end" :Width="Parent.Width * 0.33" :Height="Parent.Height" Padding="40" :Flex="7" :Background="{
HTMLCircleGradient(Width, Height, Color(99, 126, 212, 64), 0, Color(47, 55, 128, 192), 50, Color(81, 97, 158, 80), 65, color_transparent, 80),
}">
<Listen :Width="Parent.Width" :Height="Parent.Height" Hooks="player_activate, player_disconnect" Flex="8" Direction="Y">
<SymPanel Cursor="hand" :Width="Parent.Width" :PaddingY="ScreenScale(3)" :PaddingX="ScreenScale(4)" MarginBottom="15" :Background="HTMLLinearGradient(Width, Height, -90, color_transparent, 0, Color(255, 255, 255, 86), 10, Color(255, 255, 255, 109), 97, color_transparent, 100)">
There are <SymLabel Ref="Label" :Text="tostring(#player.GetAll())" FontWeight="800" /> player(s) ingame.
</SymPanel>
<For Each="_, ply in pairs(player.GetAll())" Ref="ForLoop" Name="ForLoop" FontSize="5" FontWeight="800" :Width="Parent.Width" Flex="8" Grow="true" Direction="Y" :Padding="ScreenScale(1)" :Background="Color(0, 0, 0, 180)">
<SymPanel :Click="function (p) self:Emit('ChangeValue', ply) end" :Cursor="'hand'" Ref="User" Name="Parent" Flex="4" :Width="Parent.Width" :PaddingY="ScreenScale(3)" :PaddingX="ScreenScale(4)" :Background="HTMLCircleGradient(Width, Height, Color(255, 255, 255, 86), 0, Color(255, 255, 255, 86), 95, color_transparent, 100)" :Hover:Background="HTMLCircleGradient(Width, Height, Color(207, 255, 179, 86), 0, Color(194, 255, 144, 86), 95, color_transparent, 100)">
<SymLabel Ref="Label" :Text="ply:Name()" />
</SymPanel>
</For>
</Listen>
</SymPanel>
<SymPanel Ref="Content" Grow="true" Flex="7" Direction="Y" :Height="Parent.Height" :Display="Top.Sidebar.Value ~= nil" :Padding="ScreenScale(3)">
<SymPanel><b>Name:</b> <SymLabel :Text="Top.Sidebar.Value:Name()" /></SymPanel>
<SymPanel><b>Ping:</b> <Listen Hooks="Think">
<SymLabel :Text="Top.Sidebar.Value:Ping()" />
</Listen>
</SymPanel>
</SymPanel>
</SymPanel>
- Tags (elements) are VGUI component class names.
- Works off a property based system where properties can either be primitives or functions. If they're functions, they're recalculated every layout.
- Properties prepended with a colon (such as :Test="ply:Name() .. ':'") are evaluated in Lua.
- Function environments supplied to properties include effectively a DOM, allowing you to find other elements. Properties here are inherited from their parent via metatables - so FontSize on a parent node defines the font for child nodes (unless they override it), similar to HTML and CSS rules.
- Helper functions for procedurally generating materials, including from HTML, including prettier gradients. From testing, DHTML is faster at this than drawing a gradient to your own material. HTMLMaterials are cached using weaktables to avoid unnecessary generation.
- All elements can include a padding, margin, font, backgrounds, borders, stencils.