html

HolyHTML: an HTML/CSS engine written in HolyPHP, for HolyPHP. It parses a document, computes styles, lays the boxes out, paints them onto a canvas and routes input back to the nodes. That is the whole of what a browser does for a desktop UI — without shipping a browser engine.

stdlib — embedded HTML parser CSS cascade block / inline / flex events as data

One window, one document

The window holds a single canvas. Everything inside it is a box the engine laid out; the script only reacts to the events the view hands back.

import "html";
import "ui";

$win = ui_window("Hello, HolyHTML", -1, -1, 640, 360);
$cv  = ui_add($win, 18, "", 0, 0, 640, 360);      // 18 = canvas

$view = new HtmlView($cv, 640, 360, html_theme_dark());
$view->load(
    "
Hello, HolyHTML
", ".card { padding: 16px; background-color: #252526; color: #ffffff; }"); $view->paint(); ui_run_main();

The layers

Each layer is a file, each file is testable on its own, and nothing below view knows that a canvas exists.

FileResponsibility
html/dom.hphpthe document tree and a tolerant parser (html_parse, html_set_text)
html/css.hphpselectors, specificity, the cascade (css_parse, css_add)
html/style.hphpcomputed style: colours as ints, lengths as unresolved Dim, the default stylesheet
html/layout.hphpblock, inline and flex layout, text wrapping (HtmlLayout)
html/paint.hphpthe box tree as canvas draw calls, clipping, scrolling
html/view.hphpthe facade: mount, hit-test, events, repaint (HtmlView)

Trees, selectors, and text

import "html";

$view = new HtmlView(0, 640, 360, html_theme_dark());   // 0 = headless
$view->load(
    "
one
" . "
two
", ".row { height: 24px; }"); $view->ensure(); $b = $view->find("#b"); // by id $view->set_text($b, "second"); // text is data, not a callback $rows = $view->find_all(".row"); // every match, in document order echo count($rows); // 2

The parser is deliberately forgiving: unclosed tags, stray close tags, attributes without quotes and void elements all parse. A missing </div> is not worth failing a window over.

Layout

Events are data

mouse() and key() do the bookkeeping — hover, focus, caret, scroll — and return an HtmlEvent naming the node under the pointer. The application decides what that means, which is what keeps the whole input path testable without a window.

import "html";

$view = new HtmlView(0, 400, 200, html_theme_dark());
$view->load("
open me
", ".row { height: 22px; }"); $view->ensure(); $ev = $view->mouse("click", 10, 10, 1, 0); if ($ev->node != null) { $view->add_class($ev->node, "active"); echo "clicked " . $ev->node->id; // a }

Painting, and testing the paint

Painter has a dry run: with $dry set it records every draw call as a string instead of ink. A layout can be wrong and every state test still pass; a draw list (or a pixel read back from the canvas, as examples/holyhtml_demo.hphp does) cannot.

import "html";

$view = new HtmlView(0, 320, 160, html_theme_dark());
$view->load("
x
", ".box { width: 100px; height: 40px; background-color: #007acc; }"); $view->ensure(); $paint = new Painter(0, true); $paint->paint($view->root, $view->th->bg); // second argument: the clear colour echo count($paint->log) . " draw calls";

Theming

An HtmlTheme carries the colours, the font families and the base metrics the default stylesheet uses. html_theme_dark() and html_theme_light() are the two starting points; copy one and change the fields you care about.

import "html";

$th = html_theme_light();
$th->accent = 0xd13438;
$th->fontPx = 14;

$view = new HtmlView(0, 400, 200, $th);
$view->load("
red accent
", ".card { padding: 8px; }");

Naming: the layout engine is HtmlLayout, not Layout — the ui library has a Layout of its own (the box a widget stacks in), and a HolyPHP program has one namespace. An HTML view inside a desktop window imports both.