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