DML Tutorial
From GMod Wiki
Lua: Introduction to DML |
Description: | Explains the basics of Derma Markup Language |
Original Author: | Entoros |
Created: | January 2nd, 2011 |
Contents |
Preface
This is a short introduction to Derma Markup Language, designed to teach you the basics. If you have questions about specific tags, check the documentation in the tag's file. If you have questions about anything else that isn't addressed here, ask in the release thread on Facepunch.
It is necessary that you know at least a little HTML or XML before you try to use DML. Here's a great tutorial on HTML if you need to learn or just could use a refresher: http://w3schools.com/html/default.asp
The Basics
DML syntax is just like HTML. It uses tags to create objects and to indicate the "level" of an object relative to others. Here's a simple frame tag, which you will probably use as the root element in the majority of your menus.
<frame width="100" height="100" blur="true"></frame>
Let's go through each part of the tag.
- <frame - This is the opening part of the tag. The "<" signifies a tag, and the next word tells the parser which element to create.
- width="100" height="100" - These are simple attributes, width and height. Different tags have different attributes, but generally most tags have the attributes "x", "y", "width", and "height". They are equal to a value in quotes, like a string. Attributes tell the parser additional information about the element it's creating, so you can make more complex menus.
- blur="true" - Some attributes take different values than just numbers or strings. Some need boolean values, such as the "blur" attribute which determines whether the frame brings up the background blur. For a true/false attribute, to determine whether the string is true or false it is generally passed through the G.tobool function.
- ></frame> - You always need to end the opening tag with a ">" character. The "</frame>" tells the parser that the frame has ended. Any elements after the closing tag will NOT be parented to the tag's object.
With just that, you have Derma Markup. Let's try something a little more difficult now.
Children and the Tree
The core of any DML menu is the Tree. Not like an oak tree, but a computer tree. You always start with the root element, and then branch off from there. In this case, your Tree generally contains a frame and then that frame contains more children. For example,
<frame> <panel> <label>I'm a child :(</label> </panel> </frame>
Here, the frame is our root element, meaning it always precedes the others and is not parented to anything. The top-level element in a tree is technically only parented to your screen. Then the frame has a panel element, and the panel is parented to the frame since it comes before the frame's closing tag. Lastly, the panel has a label element which is parented to the panel.
Generally, you indent your code each time you go down a level (that is, get a new parent).
Advanced Markup
Now that you have those concepts down, there's really not a whole lot more to teach you about creating DML. Here's a few things you should know though:
- Event handling: Unlike simple attributes like "x" or "width", events such as "DoClick" or "Paint" are not explicitly defined in for each tag. Rather, all attributes are checked to see if their name corresponds to an element's function. That is, if your attribute is named "DoClick", then the value of that attribute will be run on DoClick if your tag is a button. For instance,
<button DoClick="LocalPlayer():ChatPrint('You pushed the button!')"></button>
Will work just fine. Also, if you want you can define Lua functions in the script through the use of the lua tags (they function like Javascript <script> tags in HTML).
- Attribute types: While some attributes are nice and just take strings or numbers (like "width" or "title"), other attributes require different forms of input. Here's how you signify some input types:
- Vectors: Vectors come in the form of comma-delineated lists. i.e. pos="50, 10, 100"
- Booleans: As stated in the first section, booleans are parsed by G.tobool. So you could say "1", "true", etc.
- Colors: Colors are parsed like colors in HTML, using hex codes (without the # character). So, "FF0000" would be red.
Lua in DML
Lua in DML is like PHP in HTML. Sometimes it's a little hacky, but it can be necessary to do the more complex menus and layouts (for instance if you wanted to draw names from a MySQL database or something). You use Lua to create the necessary objects for the DML to run. The DML addon offers the DML library which brings a few handy functions for DML creation. Here's how you create a DML object and parse DML:
local parser = DML.New(); parser:Read(" <frame> <panel>Text goes here</panel> </frame> "); parser:Open();
Note: you would use double brackets "[[" for multiline strings, but the Wiki's Lua parser isn't perfect.
So DML.New creates a new DMLObject, an object that contains several methods for reading/writing/using DML. The ones you worry about the most are DML.New, DMLObject.Read, and DMLObject.Open. New creates the object, Read parses the DML, and Open executes it.
Developing for DML
If you're very familiar with Lua and are interested in developing for DML by creating tags or that sort of thing, I'd be happy to have you. DML is always a work in progress, so all ideas are on the table. If you take a look in the lua/autorun/elements folder in the DML addon, you can find all of the DML elements snug in their own little files. If you want to create a tag, all you have to do is create a file there and register your tag.
Let's take a look at the source for the <img> tag, or the DImage spawn, for a reference.
/*--------------------------------------------------------- Name: img Desc: A DImage. Displays a texture. Attributes: src: The material to display color: The color of the image. Example: <img src="console/gmod_logo"></img> ---------------------------------------------------------*/ local function createElement(class,parent,element) local attr,content = element.attributes,element.content; local img = vgui.Create("DImage",parent); class:SetSizePos(img,attr); img:SetImage(attr.src or "VGUI/swepicon"); img:SetImageColor(attr.color and class:HexToColor(attr.color) or color_white); class:HookEvents(element,img,attr); return img; end DML.Register("img",createElement);
Here's all the elements that make up a tag file.
- Documentation: Docs are key so people know what the heck you're doing. You should include the name of the tag, a short description, ALL attributes (and a description of those), and a quick example of the tag in action.
- The function: To create a tag you only need one thing, and that's a function (a constructor, essentially). In the constructor you create the derma element you want to use and then alter it based on the attributes. In the function, you always need to return the derma object you've created so other objects can be parented to it. This function is passed three arguments:
- class: this is the DMLObject, the class which holds the Tree and all of the methods used (Read, Open, etc.) in DML. As shown in the example, the DMLObject has the method "HexToColor" which converts a hex string into a color object. The class also has two common methods, SetSizePos which sets the size and position of object based on the attributes (because x/y/width/height are very common) and HookEvents which allows the user to override Paint or OnMousePressed without you having to do anything.
- parent: This is the parent object of type Panel which you should ALWAYS parent the derma element you're creating too (even if it's nil, it'll still work).
- element: This is a table of data created by the parser about the tag you're creating an object from. Notably, it includes the data members "attributes" and "content". Attributes is a table of all the attributes in a tag, and content is a table of the content inside a tag.
- Registration: Lastly, you need to register the function and tag name with the DML library using the DML.Register function so the parser can access it.
And voila! You can now make your own DML tag to your heart's desire. If you have any questions, make sure to contact Entoros on Facepunch.