Sciter for D
c-smile
andrew.fedoniouk at gmail.com
Thu May 14 21:29:17 UTC 2026
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;
}
```
More information about the Digitalmars-d-announce
mailing list