On Thursday, 14 May 2026 at 21:08:36 UTC, Steven Schveighoffer
wrote:
On Thursday, 14 May 2026 at 20:56:22 UTC, c-smile wrote:
I've almost done with D version of
[Sciter](https://sciter.com) SDK.
Is it interesting to anyone in principle?
Yes, this does look interesting! Never heard of sciter before.
Sciter is used in production since 2007 when it first used as UI
front end of Norton Antivirus. Since then quite a lot of
customers have joined [the club](https://sciter.com/#customers).
Does this utilize the GC, or are you doing only manual resource
management?
D app that utilizes Sciter UI frontend is a normal D application
that uses its own GC and all other bells and whistles.
That screenshot above is made of this file:
```
module dsciter;
import sciter;
import application = sciter.application;
import archive = sciter.utils.archive;
import std.stdio;
// custom drawing controller demo
import samples.drawingcontroller;
//string html = "<html><body>Hello D!</body></html>";
static blob = cast(ubyte[]) import("resources.bin");
string DCompilerName() {
version (DigitalMars) { return "DMD (Digital Mars D)"; }
version (LDC) { return "LDC (LLVM-based D Compiler)"; }
version (GNU) { return "GDC (GNU D Compiler)"; }
}
int main()
{
if(!application.start())
return -1;
archive.open(blob);
struct D {
string name;
int verMajor;
int verMinor;
}
application.globalVar("Compiler", VALUE( D(DCompilerName(),
__VERSION__ / 1000, __VERSION__ % 1000 )));
bool onClick() {
writeln("mouse CLICK");
return false;
}
Window window = new Window(Window.AS_MAIN);
//window.on(MOUSE_EVENT.TYPE.CLICK) = &onClick;
window.on(MOUSE.CLICK).at("body") = (){ writeln("mouse
CLICK"); return false;};
window.on(EVENT.CLICK).at("button#test") = (ref Element
button){ writeln("CLICK on ", button.tag); return false;};
// load HTML
// wnd.load( cast(ubyte[]) html ); // from literal HTML string
window.load("this://app/index.htm"); // from archive based on
resource blob
// show it
window.state = Window.STATE.SHOWN;
// run message pump
return application.run();
}
```
Where HTML (compiled in app body as archive at
this://app/index.htm):
```
<html window-frame="extended"
window-resizable="true"
window-minimizable="true"
window-maximizable="true"
window-blurbehind="auto"
theme="auto">
<head>
<title>Sciter greets D lang!</title>
<style>
@import url(sciter:window-chrome.css);
body {
margin:0;
}
h1, div { text-align:center; }
dl {
flow: row(dt, dd);
border-spacing: 0.5em;
text-align:start;
width:max-content;
margin:0 *;
}
dl > dt,
dl > dd { width:max-content; }
clock {
behavior: custom-drawing-controller; // custom drawing ctl,
see samples/drawingcontroller.d
display:block; // like a <div>
size:*; // spans whole available space
}
</style>
<script>
document.body.append(<dl>
<header>Compiler</header>
<dt>name:</dt> <dd>{Compiler.name}</dd>
<dt>version:</dt>
<dd>{Compiler.verMajor}.{Compiler.verMinor}</dd>
<dt>test:</dt> <dd>{adder(10,32)}</dd>
</dl>);
</script>
</head>
<body>
<h1>The D Clock</h1>
<clock/>
<div>Button to <button #test>Test</button></div>
</body>
</html>
```
And finally D resident controller of custom drawing element (the
`<clock>`):
```
module drawingcontroller;
import std.datetime;
import std.datetime.systime;
import std.utf;
import core.stdc.stdio : sprintf;
import sciter;
const float PI = 3.1415926f;
class DrawingController : ElementController
{
this(HELEMENT h) { super(h); }
override void didStart() {
startTimer(500, &this.onTick);
}
bool onTick() {
self.requestPaint(); // will cause handleDraw() to be called
return true; // keep timer ticking
}
override bool handleDraw(HELEMENT he, ref DRAW_EVENT evt)
{
if(evt.type != DRAW.CONTENT)
return false; // we are drawing only content layer
Graphics gfx = evt.gfx; // on this Graphics
RECT area = evt.area; // in this area
auto w = area.width;
auto h = area.height;
float scale = w < h? w / 300.0f: h / 300.0f;
SysTime now = Clock.currTime(); // this time
gfx.stateSave();
gfx.translate(area.left + w / 2.0f, area.top + h / 2.0f);
gfx.scale(scale,scale);
gfx.rotate(-PI/2);
gfx.lineColor(0);
gfx.lineWidth(8.0f);
gfx.lineCap(LINE_CAP.ROUND);
// Hour marks
gfx.stateSave();
gfx.lineColor(rgba(0x32,0x5F,0xA2));
for (int i = 0; i < 12; ++i) {
gfx.rotate(PI/6);
gfx.line(137.0f,0,144.0f,0);
}
gfx.stateRestore();
// Minute marks
gfx.stateSave();
gfx.lineWidth(3);
gfx.lineColor(rgba(0xA5,0x2A, 0x2A));
for (int i = 0; i < 60; ++i) {
if ( i % 5 != 0)
gfx.line(143,0,146,0);
gfx.rotate(PI/30.0f);
}
gfx.stateRestore();
// show current date
{
gfx.stateSave();
gfx.rotate(PI/2);
char[20] buffer;
int written = sprintf(buffer.ptr,
"%04d-%02d-%02d",now.year,now.month,now.day);
Text text =
Text.createForElementAndStyle(toUTF16(buffer[0..written]), self,
"font-size:10pt;color:brown"w );
gfx.draw(text, 0, 60, 5);
gfx.stateRestore();
}
uint sec = now.second;
uint min = now.minute;
uint hr = now.hour;
hr = hr >= 12 ? hr - 12 : hr;
// draw hours hand
gfx.stateSave();
gfx.rotate( hr*(PI/6) + (PI/360)*min + (PI/21600)*sec );
gfx.lineWidth(14);
gfx.lineColor(rgba(0x32,0x5F,0xA2));
gfx.line(-20,0,70,0);
gfx.stateRestore();
// draw Minute hand
gfx.stateSave();
gfx.rotate( (PI/30)*min + (PI/1800)*sec );
gfx.lineWidth(10);
gfx.lineColor(rgba(0x32,0x5F,0xA2));
gfx.line(-28,0,100,0);
gfx.stateRestore();
// draw Second hand
gfx.stateSave();
gfx.rotate(sec * PI/30);
gfx.lineColor(rgba(0xD4,0,0));
gfx.fillColor(rgba(0xD4,0,0));
gfx.lineWidth(6);
gfx.line(-30,0,83,0);
gfx.ellipse(0,0,10,10);
gfx.noFill();
gfx.ellipse(95,0,10,10);
gfx.stateRestore();
gfx.stateRestore();
return true;
}
}
static this() {
registerElementController!DrawingController("custom-drawing-controller");
// ^ for CSS:
behavior: custom-drawing-controller;
}
```