API reference
DOM, styles and queries
The document API follows the web's, in C#. If you know document.querySelector and classList, you know most of this page.
Document and elements
using DevCore.UiEngine.Dom;
using DevCore.UiEngine.Dom.Events;
var doc = view.View.Document;
var hp = doc.GetElementById("hp");
hp.TextContent = "100";
hp.ClassList.Toggle("low", health < 20);
hp.SetAttribute("data-team", "red");
doc.QuerySelectorAll(".enemy").ForEach(e => e.Remove());
hp.AddEventListener(EventType.Click, e => Debug.Log("clicked"));
bool match = hp.Matches(".bar.low");
var panel = hp.Closest(".panel");
var fill = hp.QuerySelector(".fill");
- Selectors are parsed once and cached.
QuerySelectorAll(selectors, list)fills a list of yours and allocates nothing. - Every selector the style engine supports works here, including
:has(). - For text that changes every frame,
((Text)node).SetData(chars)takes aReadOnlySpan<char>and makes no string.
Styles
element.Style is the element's inline style. It has two kinds of setters.
| Member | Meaning |
|---|---|
SetProperty(string name, string value) | Any property from CSS text. Parses and allocates: fine for occasional changes |
bool Set(CssPropertyId property, float value, CssUnit unit) | One length or number, typed: no parsing, no allocation |
bool Set(CssPropertyId property, float x, float y, CssUnit unit) | A pair: Translate, Scale |
bool Set(CssPropertyId property, float number) | A plain number: Opacity |
bool SetColor(CssPropertyId property, Rgba color) | A color |
void Remove(CssPropertyId property) | Removes the inline value |
using DevCore.UiEngine.Css.Properties; // CssPropertyId
using DevCore.UiEngine.Css.Values; // CssUnit, Rgba
marker.Style.Set(CssPropertyId.Translate, x, y, CssUnit.Px);
marker.Style.Set(CssPropertyId.Opacity, 0.5f);
bar.Style.Set(CssPropertyId.Width, 40, CssUnit.Percent);
bar.Style.SetColor(CssPropertyId.BackgroundColor, new Rgba(1, 0, 0));
marker.Style.Remove(CssPropertyId.Translate);
translate, rotate, scale and opacity set through the typed setters are written straight to the renderer: no restyle, no layout, no paint. Use them for anything that moves every frame.
Geometry
using DevCore.UiEngine.View;
ClientRect rect = hp.GetBoundingClientRect(); // X, Y, Width, Height, Left, Top, Right, Bottom
Reading geometry brings style and layout up to date first, as in a browser, so a change made just before is included.
ElementFromPoint
using DevCore.UiEngine.View;
Element top = doc.ElementFromPoint(x, y);
var slot = doc.ElementFromPoint(e.ClientX, e.ClientY)?.Closest(".slot");
doc.ElementsFromPoint(x, y, list); // every element at the point, topmost first
Both behave as in Chrome:
- The page is brought up to date first: styles, layout, paint, scroll positions and GPU-animated values.
<html>comes back over empty space, andnulloutside the view.ElementsFromPointlists every element at the point, topmost first, ending with<html>. The overload that fills your list clears it first and allocates nothing.- Elements with
pointer-events: noneare skipped, which is what makes drag and drop work.
For a per-frame check, DocumentView.HitTest(x, y) is cheaper: it reads the last frame as it is on screen.
Queries by name
Name things in markup with query:api, find them with Q and Query.
var shop = uiView.Q<Shop>("shop"); // a component: its public [State], props, methods
shop.gold += 100; // batched: the page updates once this frame
var label = uiView.Q("gold-label"); // an Element
var sword = shop.Q<ItemCard>("item-1"); // inside the shop only
var soldOut = uiView.Query<ItemCard>(className: "sold-out");
var hud = UiSystem.Q<Hud>("hud"); // over every view
uiView.Query(results, "item"); // into a list you reuse: no allocation
| Rule | |
|---|---|
T | Element or a component type. Component finds any component |
| Where | On a UIView, a DocumentView, any node, a component (only its own nodes), or UiSystem (all views) |
| Components | Found by the query:api on their tag, or as the nearest component around an element of that name |
| Names | May hold expressions (query:api="item-{item.Id}"). Several things may share a name: Q returns the first in document order |
| Classes | className narrows the search; a component's class is its root element's |
| Hidden | What is inside a false {#if}, or destroyed, is not found: Q returns null |
| Cost | Names are indexed: a search visits only what has that name and allocates nothing. Still, look things up once and keep them |