#Extended & semantic VGUI dev library

1 messages · Page 1 of 1 (latest)

acoustic aspen
#

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 &nbsp;<SymLabel Ref="Label" :Text="tostring(#player.GetAll())" FontWeight="800" />&nbsp; 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>&nbsp; <SymLabel :Text="Top.Sidebar.Value:Name()" /></SymPanel>
        <SymPanel><b>Ping:</b>&nbsp; <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.
#
  • Includes flex, which is my own take on HTML's flexbox, with justify and align set by numpad arrow directions (5 = align to centre, 9 = align to top right). There are attributes for defining gaps, grow, direction (left to right or top to bottom).
  • Includes a JS style event and propagation system.
  • Listen elements cause their children's layouts to be invalidated (and therefore their properties etc to be re-evaluated) when a hook is ran
  • Hover: selector selectively applies properties when this element or a child is hovered.
#

To explain what I mean by the flex stuff

#

Flirting whether or not just to keep it for my own use, or share more broadly

arctic seal
#

does this allow for real-time rendering?

#

i.e health bar

#

seems pretty cool

restive hound
#

why include lua to xml?
spaghetti code....

#

i see only 1 real use case: layout

acoustic aspen
acoustic aspen
restive hound
#

depends on code style 🤷‍♂️

arctic seal
#

best off creating ur own components when it comes to vgui if u want custom ui instead of hammering together a million nested dermapanels

restive hound
# restive hound why include lua to xml? spaghetti code....

btw why not make something like svelte?
eg

<script>
    local function background(Width, Height)
        return {
            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'),
        }
    end
</script>

<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=${background}>
acoustic aspen
#

I have a specific use case that I'm trying to fulfil @restive hound

#

Namely I want:

  • The power of Lua
  • The semantics, structure, speed and ease of writing HTML
  • Without the quirks of Derma which mean if you so much as look at it funny it starts layouting every frame, and getting something to sit exactly where you want it
#

I've libraryfied this - gonna spend a month or two testing it with my own things, but once I'm confident it's robust, I'll share more broadly as a VGUI framework

#

This is entirely XML:


concommand.Add("sym_dev", function ()

    if IsValid(XVGUI) then
        XVGUI:Remove()
    end

    -- I should be able to set borders, 
    -- I should be able to wrap elements, especially text.
    -- I should be able to generate XML?
    -- etc.

    local PANEL = xvgui.RegisterFromXML("SymConsole", [[
        <XPanel Ref="Top" :X="PW * 0.025" :Y="PH * 0.025" :Width="PW * 0.95" :Height="PH * 0.95" :Padding="ScreenScale(5)" Flex="7" :Background="{
                HTMLCircleGradient(Width, Height, Color(14, 17, 27, 255), 0, Color(0, 0, 0, 225), 95, Color(49, 56, 85, 225), 100),
                HTMLRepeatingLinearGradient(Width, Height, 0, color_transparent, '5px', Color(0, 0, 0, 64), '10px', color_transparent, '15px'),
            }" Direction="Y" Value="1" :On:SetValue="function (p, value) self:SetProperty('Value', value) self:InvalidateChildren() return true end"
                :Radius="ScreenScale(1)" Blur="5"
                >
                
                <XPanel Ref="Sidebar" :Width="PW" Height="32" :XBackground="Color(255, 255, 255, 32)" Flex="1" Direction="X" :Gap="ScreenScale(1)" PaddingLeft="16">
                    
                    <For Each="i=1, 4">
                        <XPanel Flex="5" :ButtonValue="i" :Selected="Value == self:GetProperty('ButtonValue')" :Click="function ()
                            if self:GetProperty('Value') == self:GetProperty('ButtonValue') then 
                                self:Emit('SetValue', nil) 
                            else 
                                self:Emit('SetValue', self:GetProperty('ButtonValue')) 
                            end 
                        end" 
                            Hover="true" 
                            Cursor="hand" 
                            Flex="5" 
                            Width="120" 
                            Selected:Height="32"
                            Height="26" 
                            Hover:Height="32" 
                            :FontColor="color_white" 
                            :FontWeight="Selected and 800 or 0" 
                            :Background="HTMLCircleGradient(Width, Height, Color(104, 114, 153, 225), 0, Color(0, 0, 0, 225), 50, Color(49, 56, 85, 225), 100)" 
                            :Hover:Background="HTMLCircleGradient(Width, Height, Color(104, 114, 153, 225), 0, Color(64, 64, 100, 225), 50, Color(104, 114, 153, 225), 100)" 
                            :Selected:Background="HTMLCircleGradient(Width, Height, Color(104, 114, 153, 225), 0, Color(64, 64, 100, 225), 50, Color(104, 114, 153, 225), 100)" 
                            TopLeftRadius="16"
                            TopRightRadius="16"
                            PaddingY="18"
                            :Border="color_white"
                            BorderSize="1"
                            >

                            <XLabel MarginTop="8" :Text="'Tab: ' .. tostring(ButtonValue)" />
                        </XPanel>
                    </For>
                    
                </XPanel>

                <XPanel Ref="Content" Flex="7" Padding="16" Direction="Y" :Width="PW" Grow="true" :Background="Color(255, 255, 255, 16)">
                    <XLabel :Text="Value and ('This is the content for tab ' .. Value) or 'No tab selected.'" />
                    <XPanel :Display="Value == '1'" Grow="true">
                        This is the content for tab 1!
                    </XPanel>
                </XPanel>
        </XPanel>
    ]])

    print("From component")
    XVGUI = vgui.Create("SymConsole")
    XVGUI:MakePopup()
end)
#

Added use case - I want to be able to use this for the Q menu as well, and I don't want to have to rewrite every VGUI element (and lose out on the ability to steal anyone elses!) in my own UI system

#

Framework itself extends beyond XML - though I'm worried about bloating...

I've also got my XPanel that effectively serve as equivalent to a HTML generic div - lots of rich functionality embedded into it

#

XPanel

  • Material/gradient generation
  • Borders and border radiuses
  • Stencils (border radiuses uses stencils)
  • Layout management
  • Font management
  • A flexbox-style implementation
  • Selectors (not quite as rich as CSS - but still pretty helpful) like Hover:Property.
  • Better more reliable auto-sizing to children
  • Wrapping
  • Better drag and drop
  • Transitions
surreal dome
#

Here's a thought or suggestion

#

Apple's SwiftUI has 3 built-in container types that make it very pleasant to lay out UI and I wish they were available in Garry's Mod

#

They are:

  • HStack - Positions its children horizontally
  • VStack - Positions its children vertically
  • ZStack - Positions its children in "depth" (Draw order)
#

Want to draw a background behind a panel? Put it in a ZStack and position the background object to be below the panel

#

Want to make a row of panels? Put them in an HStack

#

Etc.

arctic seal
#

vgui on top

#
  1. lua vgui
  2. react
  3. ms paint
  4. swiftui