Files
nmemmertandClaude Sonnet 4.6 1340bf012c Phase 4: SkyKit library + Rigel file manager; fix text centering
SkyKit (skykit/):
- Shared palette constants, font setup, and Xlib/Xft drawing utilities
- sk_fill, sk_rect, sk_line, sk_text, sk_text_clip, sk_textw, sk_baseline
- Builds as libskykit.a, consumed by Rigel and future shell components

Rigel (rigel/):
- Native C/Xlib file manager with SkyKit
- Sidebar with 8 bookmarks (Home, Desktop, Documents, Downloads, etc.)
- Editable path bar (Ctrl+L or '/' to activate, Enter to navigate)
- Sortable columns: Name / Size / Modified (click header to sort/reverse)
- Colored type badges: DIR, IMG, SND, VID, ZIP, SRC, PDF, TXT, EXE
- Double-click to open dirs or files (xdg-open)
- Keyboard: ↑↓ select, Enter open, Backspace back, Ctrl+H toggle hidden
- Right-click menu updated: "File Manager" now launches rigel

Text centering fix (deskbar + wm):
- Root cause: draw_str added glyph-specific ext.height/ext.y offset, and
  all callers used hardcoded (H-10)/2 magic numbers instead of font metrics
- Fix: draw_str now takes y as a direct baseline; str_baseline() computes
  (strip_y + strip_h + ascent - descent) / 2 for consistent centering
- WM titlebar: same fix using font_title->ascent/descent
- Affects: "Orion ▾" button, clock, date, app list, flyout menu items,
  letter icons, and all window titlebars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 15:40:03 -04:00

114 lines
4.7 KiB
C

/* skykit.c — OrionOS native UI toolkit implementation */
#include "skykit.h"
void sk_app_init(SKApp *app) {
app->dpy = XOpenDisplay(NULL);
if (!app->dpy) { fputs("skykit: cannot open display\n", stderr); exit(1); }
app->screen = DefaultScreen(app->dpy);
app->root = RootWindow(app->dpy, app->screen);
app->cmap = DefaultColormap(app->dpy, app->screen);
app->vis = DefaultVisual(app->dpy, app->screen);
app->depth = DefaultDepth(app->dpy, app->screen);
app->font_ui = XftFontOpenName(app->dpy, app->screen, SK_FONT_UI);
app->font_sm = XftFontOpenName(app->dpy, app->screen, SK_FONT_SM);
app->font_hdr = XftFontOpenName(app->dpy, app->screen, SK_FONT_HDR);
app->font_mono = XftFontOpenName(app->dpy, app->screen, SK_FONT_MONO);
if (!app->font_ui) { fputs("skykit: cannot open UI font\n", stderr); exit(1); }
if (!app->font_sm) app->font_sm = app->font_ui;
if (!app->font_hdr) app->font_hdr = app->font_ui;
if (!app->font_mono) app->font_mono = app->font_ui;
}
void sk_app_free(SKApp *app) {
if (app->font_ui) XftFontClose(app->dpy, app->font_ui);
if (app->font_sm && app->font_sm != app->font_ui) XftFontClose(app->dpy, app->font_sm);
if (app->font_hdr && app->font_hdr != app->font_ui) XftFontClose(app->dpy, app->font_hdr);
if (app->font_mono && app->font_mono != app->font_ui) XftFontClose(app->dpy, app->font_mono);
XCloseDisplay(app->dpy);
}
unsigned long sk_color(SKApp *app, unsigned long rgb) {
XColor c = {
.red = ((rgb >> 16) & 0xFF) * 257,
.green = ((rgb >> 8) & 0xFF) * 257,
.blue = ( rgb & 0xFF) * 257,
.flags = DoRed | DoGreen | DoBlue
};
XAllocColor(app->dpy, app->cmap, &c);
return c.pixel;
}
void sk_xft_color(SKApp *app, unsigned long rgb, XftColor *out) {
XRenderColor rc = {
.red = ((rgb >> 16) & 0xFF) * 257,
.green = ((rgb >> 8) & 0xFF) * 257,
.blue = ( rgb & 0xFF) * 257,
.alpha = 0xFFFF
};
XftColorAllocValue(app->dpy, app->vis, app->cmap, &rc, out);
}
void sk_fill(SKPainter *p, unsigned long rgb, int x, int y, int w, int h) {
XSetForeground(p->app->dpy, p->gc, sk_color(p->app, rgb));
XFillRectangle(p->app->dpy, p->drw, p->gc, x, y, (unsigned)w, (unsigned)h);
}
void sk_rect(SKPainter *p, unsigned long rgb, int x, int y, int w, int h) {
XSetForeground(p->app->dpy, p->gc, sk_color(p->app, rgb));
XDrawRectangle(p->app->dpy, p->drw, p->gc, x, y, (unsigned)(w-1), (unsigned)(h-1));
}
void sk_line(SKPainter *p, unsigned long rgb, int x1, int y1, int x2, int y2) {
XSetForeground(p->app->dpy, p->gc, sk_color(p->app, rgb));
XDrawLine(p->app->dpy, p->drw, p->gc, x1, y1, x2, y2);
}
void sk_text(SKPainter *p, XftFont *font, unsigned long rgb,
int x, int y, const char *s) {
if (!s || !s[0]) return;
XftDraw *xd = XftDrawCreate(p->app->dpy, p->drw, p->app->vis, p->app->cmap);
XftColor col; sk_xft_color(p->app, rgb, &col);
XftDrawStringUtf8(xd, &col, font, x, y, (const FcChar8 *)s, (int)strlen(s));
XftColorFree(p->app->dpy, p->app->vis, p->app->cmap, &col);
XftDrawDestroy(xd);
}
void sk_text_clip(SKPainter *p, XftFont *font, unsigned long rgb,
int x, int y, const char *s,
int cx, int cy, int cw, int ch) {
if (!s || !s[0]) return;
XftDraw *xd = XftDrawCreate(p->app->dpy, p->drw, p->app->vis, p->app->cmap);
XRectangle clip = { (short)cx, (short)cy, (unsigned short)cw, (unsigned short)ch };
XftDrawSetClipRectangles(xd, 0, 0, &clip, 1);
XftColor col; sk_xft_color(p->app, rgb, &col);
XftDrawStringUtf8(xd, &col, font, x, y, (const FcChar8 *)s, (int)strlen(s));
XftColorFree(p->app->dpy, p->app->vis, p->app->cmap, &col);
XftDrawSetClip(xd, None);
XftDrawDestroy(xd);
}
int sk_textw(SKApp *app, XftFont *font, const char *s) {
if (!s || !s[0]) return 0;
XGlyphInfo ext;
XftTextExtentsUtf8(app->dpy, font, (const FcChar8 *)s, (int)strlen(s), &ext);
return ext.xOff;
}
int sk_baseline(XftFont *font, int y, int h) {
return y + (h + font->ascent - font->descent) / 2;
}
int sk_in_rect(int mx, int my, int x, int y, int w, int h) {
return mx >= x && mx < x + w && my >= y && my < y + h;
}
void sk_draw_btn(SKPainter *p, SKApp *app, XftFont *font, const SKBtn *b,
int hovered, unsigned long bg, unsigned long bghov) {
unsigned long bbg = hovered ? bghov : bg;
sk_fill(p, bbg, b->x, b->y, b->w, b->h);
sk_rect(p, SK_BORDER, b->x, b->y, b->w, b->h);
int tx = b->x + (b->w - sk_textw(app, font, b->label)) / 2;
sk_text(p, font, SK_TEXT_HDR, tx, sk_baseline(font, b->y, b->h), b->label);
}