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.
HTML5, CSS3, DOM, JS for a GUI application is absolutely not
necessary. However I do understand why people want to use them -
these are anywhere. People are familiar with them, etc...

To run, save to clock.d and then `dub run --single clock.d`, and
soon you will get the application window similar to the
screenshot above, assuming you have GTK4 installed.
PS. I could as easily embed entire WebKit into my giD-based
application and have all the above, and more, so what do D
developers get with sciter that separates it from the rest of the
crowd?
Code:
```d
/+dub.sdl:
name "gidex-clock"
dependency "gid:gtk4" version="*"
targetType "executable"
+/
/// Analog clock application using gid:gtk4 and Cairo.
/// Run with: dub run --single src/gidex/clock.d
module gidex.clock;
import std.datetime.systime;
import core.stdc.stdio : sprintf;
import gtk.application;
import gtk.application_window;
import gtk.box;
import gtk.label;
import gtk.types : Orientation;
import gio.types : ApplicationFlags;
import gio.application : GioApp = Application;
import gtk.drawing_area;
import cairo.context;
import glib.global : timeoutAdd;
enum PI = 3.1415926f;
class ClockWindow : ApplicationWindow {
DrawingArea drawingArea;
this(gtk.application.Application app) {
super(app);
setTitle("giD Clock");
setDefaultSize(400, 450);
auto box = new Box(Orientation.Vertical, 8);
auto titleLbl = new Label("The D Clock");
titleLbl.setMarginTop(8);
box.append(titleLbl);
drawingArea = new DrawingArea();
drawingArea.setContentWidth(300);
drawingArea.setContentHeight(300);
drawingArea.setMarginStart(16);
drawingArea.setMarginEnd(16);
drawingArea.setVexpand(true);
drawingArea.setHexpand(true);
drawingArea.setDrawFunc(&drawClock);
box.append(drawingArea);
setChild(box);
// Tick every second to keep the clock hands moving
timeoutAdd(0, 1000, {
drawingArea.queueDraw();
return true;
});
}
void drawClock(DrawingArea area, Context cr, int width, int
height) {
import std.math : cos, sin;
import cairo.types;
float cx = width / 2.0f;
float cy = height / 2.0f;
float radius = (cx < cy ? cx : cy) * 0.9f;
// White clock face
cr.setSourceRgba(1.0, 1.0, 1.0, 1.0);
cr.arc(cx, cy, radius, 0, 2 * PI);
cr.fill();
// Clock border
cr.setSourceRgba(0.2, 0.2, 0.2, 1.0);
cr.setLineWidth(radius * 0.02);
cr.arc(cx, cy, radius, 0, 2 * PI);
cr.stroke();
cr.setLineCap(LineCap.Round);
// Hour marks
cr.setSourceRgba(0.196, 0.373, 0.635, 1.0); // #325FA2
cr.setLineWidth(radius * 0.04);
foreach (i; 0 .. 12) {
float angle = i * PI / 6.0f - PI / 2.0f;
float inner = radius * 0.85f;
float outer = radius * 0.92f;
cr.moveTo(cx + inner * cos(angle), cy + inner *
sin(angle));
cr.lineTo(cx + outer * cos(angle), cy + outer *
sin(angle));
cr.stroke();
}
// Minute marks
cr.setSourceRgba(0.647, 0.165, 0.165, 1.0); // #A52A2A
cr.setLineWidth(radius * 0.015);
foreach (i; 0 .. 60) {
if (i % 5 != 0) {
float angle = i * PI / 30.0f - PI / 2.0f;
float inner = radius * 0.9f;
float outer = radius * 0.92f;
cr.moveTo(cx + inner * cos(angle), cy + inner *
sin(angle));
cr.lineTo(cx + outer * cos(angle), cy + outer *
sin(angle));
cr.stroke();
}
}
// Current time
SysTime now = Clock.currTime();
uint sec = now.second;
uint min = now.minute;
uint hr = now.hour;
hr = hr >= 12 ? hr - 12 : hr;
// Date text
{
char[20] buf;
int written = sprintf(buf.ptr, "%04d-%02d-%02d",
now.year, now.month, now.day);
string dateStr = buf[0 .. written].idup;
cr.setSourceRgba(0.647, 0.165, 0.165, 1.0); // brown
cr.selectFontFace("Sans", FontSlant.Normal,
FontWeight.Normal);
cr.setFontSize(radius * 0.12);
TextExtents te;
cr.textExtents(dateStr, te);
cr.moveTo(cx - te.width / 2.0, cy + radius * 0.35);
cr.showText(dateStr);
}
// Hour hand
{
float angle = (hr * (PI / 6.0f) + (PI / 360.0f) * min
+ (PI / 21600.0f) * sec) - PI / 2.0f;
cr.setSourceRgba(0.196, 0.373, 0.635, 1.0);
cr.setLineWidth(radius * 0.06);
cr.setLineCap(LineCap.Round);
cr.moveTo(cx - radius * 0.12 * cos(angle), cy -
radius * 0.12 * sin(angle));
cr.lineTo(cx + radius * 0.5 * cos(angle), cy + radius
* 0.5 * sin(angle));
cr.stroke();
}
// Minute hand
{
float angle = ((PI / 30.0f) * min + (PI / 1800.0f) *
sec) - PI / 2.0f;
cr.setSourceRgba(0.196, 0.373, 0.635, 1.0);
cr.setLineWidth(radius * 0.04);
cr.setLineCap(LineCap.Round);
cr.moveTo(cx - radius * 0.15 * cos(angle), cy -
radius * 0.15 * sin(angle));
cr.lineTo(cx + radius * 0.7 * cos(angle), cy + radius
* 0.7 * sin(angle));
cr.stroke();
}
// Second hand
{
float angle = sec * PI / 30.0f - PI / 2.0f;
cr.setSourceRgba(0.831, 0.0, 0.0, 1.0); // #D40000
cr.setLineWidth(radius * 0.02);
cr.setLineCap(LineCap.Round);
cr.moveTo(cx - radius * 0.18 * cos(angle), cy -
radius * 0.18 * sin(angle));
cr.lineTo(cx + radius * 0.6 * cos(angle), cy + radius
* 0.6 * sin(angle));
cr.stroke();
// Small circle at tip
cr.arc(cx + radius * 0.6 * cos(angle), cy + radius *
0.6 * sin(angle), radius * 0.03, 0, 2 * PI);
cr.fill();
// Center dot
cr.arc(cx, cy, radius * 0.04, 0, 2 * PI);
cr.fill();
}
}
}
class ClockApplication : gtk.application.Application {
ClockWindow window;
this() {
super("org.example.gidex-clock",
ApplicationFlags.DefaultFlags);
connectActivate(&onActivate);
}
void onActivate(GioApp app) {
if (!window)
window = new ClockWindow(this);
window.present();
}
}
void main(string[] args) {
auto app = new ClockApplication;
app.run(args);
}
```