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;
}

```


Reply via email to