133 lines
5.2 KiB
C
133 lines
5.2 KiB
C
// widgetBox.c — VBox, HBox, and Frame container widgets
|
|
//
|
|
// VBox and HBox are the primary layout containers. They have no visual
|
|
// representation of their own — they exist purely to arrange children
|
|
// vertically or horizontally. The actual layout algorithm lives in
|
|
// widgetLayout.c (widgetCalcMinSizeBox / widgetLayoutBox) which handles
|
|
// weight-based space distribution, spacing, padding, and alignment.
|
|
//
|
|
// VBox and HBox are distinguished by a flag (WCLASS_HORIZ_CONTAINER) in
|
|
// the class table rather than having separate code. This keeps the layout
|
|
// engine unified — the same algorithm works in both orientations by
|
|
// swapping which axis is "major" vs "minor".
|
|
//
|
|
// Frame is a labeled grouping box with a Motif-style beveled border.
|
|
// It acts as a VBox for layout purposes (children stack vertically inside
|
|
// the frame's padded interior). The title text sits centered vertically
|
|
// on the top border line, with a small background-filled gap to visually
|
|
// "break" the border behind the title — this is the classic Win3.1/Motif
|
|
// group box appearance.
|
|
|
|
#include "widgetInternal.h"
|
|
|
|
|
|
// ============================================================
|
|
// widgetFramePaint
|
|
// ============================================================
|
|
|
|
// Paint the frame border and optional title. The border is offset down by
|
|
// half the font height so the title text can sit centered on the top edge.
|
|
// This creates the illusion of the title "interrupting" the border — a
|
|
// background-colored rectangle is drawn behind the title to erase the
|
|
// border pixels, then the title is drawn on top.
|
|
//
|
|
// Three border styles are supported:
|
|
// FrameFlatE — single-pixel solid color outline
|
|
// FrameInE — Motif "groove" (inset bevel: shadow-then-highlight)
|
|
// FrameOutE — Motif "ridge" (outset bevel: highlight-then-shadow)
|
|
// The groove/ridge are each two nested 1px bevels with swapped colors.
|
|
void widgetFramePaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
|
|
int32_t fb = widgetFrameBorderWidth(w);
|
|
int32_t boxY = w->y + font->charHeight / 2;
|
|
int32_t boxH = w->h - font->charHeight / 2;
|
|
uint32_t fg = w->fgColor ? w->fgColor : colors->contentFg;
|
|
uint32_t bg = w->bgColor ? w->bgColor : colors->contentBg;
|
|
|
|
if (w->as.frame.style == FrameFlatE) {
|
|
// Flat: solid color rectangle outline
|
|
uint32_t fc = w->as.frame.color ? w->as.frame.color : colors->windowShadow;
|
|
|
|
drawHLine(d, ops, w->x, boxY, w->w, fc);
|
|
drawHLine(d, ops, w->x, boxY + boxH - 1, w->w, fc);
|
|
drawVLine(d, ops, w->x, boxY, boxH, fc);
|
|
drawVLine(d, ops, w->x + w->w - 1, boxY, boxH, fc);
|
|
} else {
|
|
// Beveled groove/ridge: two nested 1px bevels
|
|
BevelStyleT outer;
|
|
BevelStyleT inner;
|
|
|
|
if (w->as.frame.style == FrameInE) {
|
|
outer.highlight = colors->windowShadow;
|
|
outer.shadow = colors->windowHighlight;
|
|
inner.highlight = colors->windowHighlight;
|
|
inner.shadow = colors->windowShadow;
|
|
} else {
|
|
outer.highlight = colors->windowHighlight;
|
|
outer.shadow = colors->windowShadow;
|
|
inner.highlight = colors->windowShadow;
|
|
inner.shadow = colors->windowHighlight;
|
|
}
|
|
|
|
outer.face = 0;
|
|
outer.width = 1;
|
|
inner.face = 0;
|
|
inner.width = 1;
|
|
drawBevel(d, ops, w->x, boxY, w->w, boxH, &outer);
|
|
drawBevel(d, ops, w->x + 1, boxY + 1, w->w - 2, boxH - 2, &inner);
|
|
}
|
|
|
|
// Draw title centered vertically on the top border line
|
|
if (w->as.frame.title && w->as.frame.title[0]) {
|
|
int32_t titleW = textWidthAccel(font, w->as.frame.title);
|
|
int32_t titleX = w->x + DEFAULT_PADDING + fb;
|
|
int32_t titleY = boxY + (fb - font->charHeight) / 2;
|
|
|
|
rectFill(d, ops, titleX - 2, titleY,
|
|
titleW + 4, font->charHeight, bg);
|
|
|
|
if (!w->enabled) {
|
|
drawTextAccelEmbossed(d, ops, font, titleX, titleY, w->as.frame.title, colors);
|
|
} else {
|
|
drawTextAccel(d, ops, font, titleX, titleY, w->as.frame.title, fg, bg, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// wgtFrame
|
|
// ============================================================
|
|
|
|
// Create a Frame container. The title string supports accelerator keys
|
|
// (prefixed with '&') which are parsed by accelParse. The title pointer
|
|
// is stored directly (not copied) — the caller must ensure it remains valid.
|
|
WidgetT *wgtFrame(WidgetT *parent, const char *title) {
|
|
WidgetT *w = widgetAlloc(parent, WidgetFrameE);
|
|
|
|
if (w) {
|
|
w->as.frame.title = title;
|
|
w->as.frame.style = FrameInE;
|
|
w->as.frame.color = 0;
|
|
w->accelKey = accelParse(title);
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// wgtHBox
|
|
// ============================================================
|
|
|
|
WidgetT *wgtHBox(WidgetT *parent) {
|
|
return widgetAlloc(parent, WidgetHBoxE);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// wgtVBox
|
|
// ============================================================
|
|
|
|
WidgetT *wgtVBox(WidgetT *parent) {
|
|
return widgetAlloc(parent, WidgetVBoxE);
|
|
}
|