From d10f922690b4b8cb3bd742ea8ecb51fa29c3abe8 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Mon, 16 Mar 2026 20:45:29 -0500 Subject: [PATCH] Documentation updated. --- dvx/README.md | 1048 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 755 insertions(+), 293 deletions(-) diff --git a/dvx/README.md b/dvx/README.md index efcf718..5bd87d5 100644 --- a/dvx/README.md +++ b/dvx/README.md @@ -7,8 +7,9 @@ Motif-style beveled chrome, dirty-rectangle compositing, draggable and resizable windows, dropdown menus, scrollbars, and a declarative widget/layout system with buttons, checkboxes, radios, text inputs, dropdowns, combo boxes, sliders, spinners, progress bars, tab controls, tree views, list views, -scroll panes, toolbars, status bars, images, image buttons, drawable canvases, -password inputs, masked/formatted inputs, and an ANSI BBS terminal emulator. +scroll panes, splitters, toolbars, status bars, images, image buttons, +drawable canvases, password inputs, masked/formatted inputs, and an ANSI BBS +terminal emulator. ## Building @@ -49,11 +50,23 @@ Supporting files: | `dvxFont.h` | Built-in 8x14 bitmap font glyph data | | `dvxCursor.h` | Mouse cursor bitmask data (5 shapes) | | `dvxPalette.h` | Default VGA palette for 8-bit mode | +| `dvxDialog.h` | Modal dialogs (message box, file dialog) | | `dvxIcon.c` | stb_image implementation unit (BMP/PNG/JPEG/GIF) | | `dvxImageWrite.c` | stb_image_write implementation unit (PNG export) | | `thirdparty/stb_image.h` | Third-party single-header image loader | | `thirdparty/stb_image_write.h` | Third-party single-header image writer | +The platform abstraction lives in `platform/`: + +| File | Purpose | +|------|---------| +| `platform/dvxPlatform.h` | OS/CPU-neutral interface: video, input, span ops, filename validation | +| `platform/dvxPlatformDos.c` | DOS/DJGPP implementation: VESA, DPMI, INT 16h/33h, rep stosl/movsl | + +To port DV/X to a new platform, implement a new `dvxPlatformXxx.c` against +`platform/dvxPlatform.h` and swap it in the Makefile. No other files need +modification. + The widget system lives in `widgets/`: | File | Purpose | @@ -72,6 +85,8 @@ by a per-type vtable (`WidgetClassT`) rather than switch statements in the core files. Adding a new widget type requires only a new `.c` file and an entry in the class table. +--- + ## Quick start ```c @@ -137,7 +152,7 @@ int main(void) { WidgetT *root = wgtInitWindow(&ctx, win); WidgetT *lbl = wgtLabel(root, "Ready."); - strncpy(lbl->name, "status", MAX_WIDGET_NAME); + wgtSetName(lbl, "status"); wgtHSeparator(root); @@ -222,6 +237,21 @@ After creation, set `win->userData` and install callbacks: Mouse/key coordinates are relative to the content area. `buttons` is a bitmask (bit 0 = left, bit 1 = right, bit 2 = middle). +Example: + +```c +static void onClose(WindowT *win) { + AppContextT *ctx = (AppContextT *)win->userData; + dvxDestroyWindow(ctx, win); +} + +WindowT *win = dvxCreateWindow(&ctx, "My Window", 50, 50, 400, 300, true); +win->userData = &ctx; +win->onClose = onClose; +win->onPaint = myPaintHandler; +win->onMenu = myMenuHandler; +``` + ```c void dvxDestroyWindow(AppContextT *ctx, WindowT *win); ``` @@ -239,6 +269,22 @@ Load a BMP, TGA, or PNG image and assign it as the window's minimized icon. The image is converted to the display pixel format and scaled to 64x64. Returns 0 on success. +```c +void dvxMinimizeWindow(AppContextT *ctx, WindowT *win); +void dvxMaximizeWindow(AppContextT *ctx, WindowT *win); +void dvxFitWindow(AppContextT *ctx, WindowT *win); +``` +`dvxFitWindow` resizes the window to fit its widget tree's natural size. + +### Window arrangement + +```c +dvxCascadeWindows(&ctx); // staggered cascade +dvxTileWindows(&ctx); // grid tile +dvxTileWindowsH(&ctx); // side-by-side +dvxTileWindowsV(&ctx); // top-to-bottom +``` + ### Invalidation ```c @@ -249,6 +295,22 @@ void dvxInvalidateWindow(AppContextT *ctx, WindowT *win); Mark a region (or the entire content area) as needing repaint. The compositor flushes dirty rectangles to the LFB on the next frame. +### Clipboard + +```c +dvxClipboardCopy("Hello", 5); + +int32_t len; +const char *text = dvxClipboardGet(&len); +``` + +### Screenshots + +```c +dvxScreenshot(&ctx, "screen.png"); // entire screen +dvxWindowScreenshot(&ctx, win, "window.png"); // single window content +``` + ### Accessors ```c @@ -288,36 +350,110 @@ the LFB. ## Menu bars ```c -MenuBarT *wmAddMenuBar(WindowT *win); +WindowT *win = dvxCreateWindow(&ctx, "Menus", 50, 50, 400, 300, true); +win->onMenu = onMenuCb; + +MenuBarT *bar = wmAddMenuBar(win); + +MenuT *fileMenu = wmAddMenu(bar, "&File"); +wmAddMenuItem(fileMenu, "&New", CMD_FILE_NEW); +wmAddMenuItem(fileMenu, "&Open...", CMD_FILE_OPEN); +wmAddMenuItem(fileMenu, "&Save", CMD_FILE_SAVE); +wmAddMenuSeparator(fileMenu); +wmAddMenuItem(fileMenu, "E&xit", CMD_FILE_EXIT); + +MenuT *viewMenu = wmAddMenu(bar, "&View"); +wmAddMenuCheckItem(viewMenu, "Tool&bar", CMD_VIEW_TOOLBAR, true); +wmAddMenuCheckItem(viewMenu, "&Status Bar", CMD_VIEW_STATUSBAR, true); +wmAddMenuSeparator(viewMenu); +wmAddMenuRadioItem(viewMenu, "S&mall", CMD_VIEW_SIZE_SMALL, false); +wmAddMenuRadioItem(viewMenu, "Me&dium", CMD_VIEW_SIZE_MED, true); +wmAddMenuRadioItem(viewMenu, "&Large", CMD_VIEW_SIZE_LARGE, false); + +// Cascading submenu +MenuT *zoomMenu = wmAddSubMenu(viewMenu, "&Zoom"); +wmAddMenuItem(zoomMenu, "Zoom &In", CMD_ZOOM_IN); +wmAddMenuItem(zoomMenu, "Zoom &Out", CMD_ZOOM_OUT); + +wmUpdateContentRect(win); +wmReallocContentBuf(win, &ctx.display); ``` -Add a menu bar to a window. Call `wmUpdateContentRect()` and -`wmReallocContentBuf()` after adding the menu bar (the menu bar reduces -the content area). + +The `&` prefix marks accelerator keys -- Alt+F opens "&File". +Up to 8 menus per bar, 16 items per menu, submenus nested up to 4 deep. + +Menu callback: ```c -MenuT *wmAddMenu(MenuBarT *bar, const char *label); +static void onMenuCb(WindowT *win, int32_t menuId) { + AppContextT *ctx = (AppContextT *)win->userData; + + switch (menuId) { + case CMD_FILE_EXIT: + dvxQuit(ctx); + break; + } +} ``` -Add a top-level menu to the bar. Returns a `MenuT *` to populate with items. + +### Context menus + +Right-click menus can be attached to any window or widget: ```c -void wmAddMenuItem(MenuT *menu, const char *label, int32_t id); -void wmAddMenuSeparator(MenuT *menu); +// Window-level context menu +MenuT *ctxMenu = wmCreateMenu(); +wmAddMenuItem(ctxMenu, "Cu&t", CMD_CUT); +wmAddMenuItem(ctxMenu, "&Copy", CMD_COPY); +wmAddMenuItem(ctxMenu, "&Paste", CMD_PASTE); +wmAddMenuSeparator(ctxMenu); +wmAddMenuItem(ctxMenu, "&Properties...", CMD_PROPS); +win->contextMenu = ctxMenu; + +// Widget-level context menu +WidgetT *lb = wgtListBox(root); +MenuT *lbCtx = wmCreateMenu(); +wmAddMenuItem(lbCtx, "&Delete", CMD_DELETE); +wmAddMenuItem(lbCtx, "Select &All", CMD_SELALL); +lb->contextMenu = lbCtx; ``` -Add items to a menu. `id` is an application-defined command passed to -`onMenu`. Up to 16 items per menu, 8 menus per bar. + +Context menus route through the window's `onMenu` callback. The caller +owns the `MenuT` (not freed by the widget system). + +--- + +## Accelerator tables + +Global hotkeys routed through the window's `onMenu` callback: + +```c +AccelTableT *accel = dvxCreateAccelTable(); +dvxAddAccel(accel, 'N', ACCEL_CTRL, CMD_FILE_NEW); +dvxAddAccel(accel, 'O', ACCEL_CTRL, CMD_FILE_OPEN); +dvxAddAccel(accel, 'S', ACCEL_CTRL, CMD_FILE_SAVE); +dvxAddAccel(accel, 'Q', ACCEL_CTRL, CMD_FILE_EXIT); +dvxAddAccel(accel, KEY_F1, 0, CMD_HELP_ABOUT); +win->accelTable = accel; +``` + +Modifier constants: `ACCEL_SHIFT` (0x03), `ACCEL_CTRL` (0x04), `ACCEL_ALT` (0x08). + +Key constants for extended keys: `KEY_F1`--`KEY_F12`, `KEY_INSERT`, `KEY_DELETE`, +`KEY_HOME`, `KEY_END`, `KEY_PGUP`, `KEY_PGDN`. + +Up to 32 entries per table. The table is freed with `dvxFreeAccelTable()`. --- ## Scrollbars ```c -ScrollbarT *wmAddVScrollbar(WindowT *win, int32_t min, int32_t max, - int32_t pageSize); -ScrollbarT *wmAddHScrollbar(WindowT *win, int32_t min, int32_t max, - int32_t pageSize); +wmAddVScrollbar(win, 0, 100, 25); // vertical, range 0-100, page size 25 +wmAddHScrollbar(win, 0, 100, 25); // horizontal +wmUpdateContentRect(win); +wmReallocContentBuf(win, &ctx.display); ``` -Add a scrollbar to a window. `pageSize` controls the thumb size relative to -the range. Call `wmUpdateContentRect()` and `wmReallocContentBuf()` afterward. The `onScroll` callback fires when the user drags the thumb or clicks the arrow buttons / trough. @@ -327,6 +463,52 @@ manually. --- +## Dialogs (`dvxDialog.h`) + +### Message box + +```c +int32_t result = dvxMessageBox(&ctx, "Confirm", + "Are you sure you want to exit?", + MB_YESNO | MB_ICONQUESTION); + +if (result == ID_YES) { + dvxQuit(&ctx); +} +``` + +Button flags: `MB_OK`, `MB_OKCANCEL`, `MB_YESNO`, `MB_YESNOCANCEL`, `MB_RETRYCANCEL`. + +Icon flags: `MB_ICONINFO`, `MB_ICONWARNING`, `MB_ICONERROR`, `MB_ICONQUESTION`. + +Return values: `ID_OK`, `ID_CANCEL`, `ID_YES`, `ID_NO`, `ID_RETRY`. + +### File dialog + +```c +static const FileFilterT filters[] = { + {"All Files (*.*)", "*.*"}, + {"Text Files (*.txt)", "*.txt"}, + {"Bitmap Files (*.bmp)", "*.bmp"} +}; + +char path[260]; + +if (dvxFileDialog(&ctx, "Open File", FD_OPEN, NULL, filters, 3, path, sizeof(path))) { + // path contains the selected filename +} + +if (dvxFileDialog(&ctx, "Save As", FD_SAVE, NULL, filters, 3, path, sizeof(path))) { + // path contains the chosen filename +} +``` + +Flags: `FD_OPEN`, `FD_SAVE`. Pass `NULL` for `initialDir` to use the +current directory. Filename validation is platform-specific (DOS 8.3 on +the DOS platform). + +--- + ## Video and drawing (`dvxVideo.h`, `dvxDraw.h`) These are lower-level APIs. Application code typically only needs `packColor`. @@ -362,6 +544,15 @@ int32_t textWidth(const BitmapFontT *font, const char *text); Draw text using the built-in 8-pixel-wide bitmap font. `opaque` controls whether background pixels are drawn. `drawChar` returns the advance width. +```c +void drawTextN(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, + int32_t x, int32_t y, const char *text, int32_t count, + uint32_t fg, uint32_t bg, bool opaque); +``` +Bulk-render exactly `count` characters. Much faster than calling `drawChar` +per character because clip bounds are computed once and background is filled +in bulk. + ```c void drawTermRow(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, int32_t cols, @@ -464,6 +655,28 @@ WidgetT *wgtFrame(WidgetT *parent, const char *title); // titled border Containers hold child widgets and control layout direction. `wgtFrame` draws a styled border with a title label and lays out children vertically. +Example: + +```c +WidgetT *root = wgtInitWindow(&ctx, win); + +WidgetT *frame = wgtFrame(root, "User Input"); +WidgetT *row1 = wgtHBox(frame); +wgtLabel(row1, "Name:"); +wgtTextInput(row1, 64); + +WidgetT *row2 = wgtHBox(frame); +wgtLabel(row2, "Email:"); +wgtTextInput(row2, 128); + +WidgetT *btnRow = wgtHBox(root); +btnRow->align = AlignEndE; +wgtButton(btnRow, "OK"); +wgtButton(btnRow, "Cancel"); + +wgtInvalidate(root); +``` + Frame styles (set `w->as.frame.style` after creation): | Style | Description | @@ -472,8 +685,6 @@ Frame styles (set `w->as.frame.style` after creation): | `FrameOutE` | Beveled outward / raised | | `FrameFlatE` | Solid color line; set `w->as.frame.color` (0 = windowShadow) | -The title text is vertically centered on the top border line. - Container properties (set directly on the returned `WidgetT *`): | Field | Type | Default | Description | @@ -490,92 +701,174 @@ Alignment values: | `AlignCenterE` | center | center | | `AlignEndE` | right | bottom | -### Widgets +### Label ```c WidgetT *wgtLabel(WidgetT *parent, const char *text); ``` -Static text label. Sized to fit its text. +Static text label. Sized to fit its text. Supports `&` accelerator markers. + +```c +WidgetT *lbl = wgtLabel(root, "&Status:"); +wgtSetText(lbl, "Connected"); // change text later +``` + +### Button ```c WidgetT *wgtButton(WidgetT *parent, const char *text); ``` -Beveled push button with standard press/release behavior: the button -visually depresses on mouse-down, tracks the cursor while held, and -fires `onClick` on release if still over the button. Dragging off the -button cancels the click. +Beveled push button. Visually depresses on mouse-down, tracks the cursor +while held, fires `onClick` on release if still over the button. + +```c +static void onOkClick(WidgetT *w) { + // handle button press +} + +WidgetT *btn = wgtButton(root, "&OK"); +btn->onClick = onOkClick; +``` + +### Checkbox ```c WidgetT *wgtCheckbox(WidgetT *parent, const char *text); ``` -Toggle checkbox. Read/write `w->as.checkbox.checked`. Set `onChange` to -be notified. +Toggle checkbox. Read/write `w->as.checkbox.checked`. + +```c +WidgetT *chk = wgtCheckbox(root, "Enable feature &A"); +chk->as.checkbox.checked = true; // pre-check +chk->onChange = onCheckChanged; // notified on toggle +``` + +### Radio buttons ```c WidgetT *wgtRadioGroup(WidgetT *parent); WidgetT *wgtRadio(WidgetT *parent, const char *text); ``` -Radio buttons with diamond-shaped indicators (visually distinct from -checkboxes). Create a group, then add options as children. The group -tracks the selected index in `w->as.radioGroup.selectedIdx`. Set -`onChange` on the group to be notified. Radio indices are auto-assigned. +Radio buttons with diamond-shaped indicators. Create a group, then add +options as children. The group tracks the selected index. + +```c +WidgetT *rg = wgtRadioGroup(root); +wgtRadio(rg, "Option &1"); +wgtRadio(rg, "Option &2"); +wgtRadio(rg, "Option &3"); +rg->onChange = onRadioChanged; + +// Read selection: +int32_t idx = rg->as.radioGroup.selectedIdx; +``` + +### TextInput ```c WidgetT *wgtTextInput(WidgetT *parent, int32_t maxLen); ``` Single-line text input field. `maxLen` is the buffer capacity. Default -weight is 100 (stretches to fill). Set `onChange` to be notified on edits. +weight is 100 (stretches to fill). + +```c +WidgetT *row = wgtHBox(root); +wgtLabel(row, "&Name:"); +WidgetT *input = wgtTextInput(row, 64); +wgtSetText(input, "Default text"); +input->onChange = onTextChanged; + +// Read text: +const char *text = wgtGetText(input); +``` Editing features: cursor movement (arrows, Home, End), insert/overwrite, selection (Shift+arrows, Shift+Home/End, Ctrl+A, double-click word, triple-click all), clipboard (Ctrl+C copy, Ctrl+V paste, Ctrl+X cut), single-level undo (Ctrl+Z), and horizontal scrolling when text exceeds -the visible width. Mouse drag selection auto-scrolls when the cursor -reaches the edge of the field. +the visible width. + +### PasswordInput ```c WidgetT *wgtPasswordInput(WidgetT *parent, int32_t maxLen); ``` -Password input field. Identical to `wgtTextInput` except characters are -displayed as bullets (CP437 `\xF9`). Copy and cut to clipboard are -disabled; paste is allowed. All other editing features work normally. +Identical to `wgtTextInput` except characters are displayed as bullets. +Copy and cut to clipboard are disabled; paste is allowed. + +```c +WidgetT *pw = wgtPasswordInput(root, 32); +``` + +### MaskedInput ```c WidgetT *wgtMaskedInput(WidgetT *parent, const char *mask); ``` -Formatted input field with a fixed mask pattern. The mask string defines -the format: `#` accepts a digit, `A` accepts a letter, `*` accepts any -printable character. All other characters in the mask are literals that -are displayed but cannot be edited. The cursor skips over literal -positions automatically. - -Example masks: -- `"(###) ###-####"` -- US phone number -- `"##/##/####"` -- date -- `"###-##-####"` -- SSN - -The buffer always contains the full formatted text including literals. -`wgtGetText()` returns the formatted string. Supports selection -(Shift+arrows, Ctrl+A), clipboard (Ctrl+C, Ctrl+V, Ctrl+X), and -single-level undo (Ctrl+Z). The mask string must remain valid for the -widget's lifetime. +Formatted input field. Mask characters: `#` = digit, `A` = letter, +`*` = any printable. All other characters are fixed literals. ```c -WidgetT *wgtListBox(WidgetT *parent); +wgtMaskedInput(root, "(###) ###-####"); // US phone +wgtMaskedInput(root, "##/##/####"); // date +wgtMaskedInput(root, "###-##-####"); // SSN ``` -List box (basic -- set items with `wgtListBoxSetItems()`). + +The mask string must remain valid for the widget's lifetime. + +### TextArea ```c WidgetT *wgtTextArea(WidgetT *parent, int32_t maxLen); ``` -Multi-line text editor with vertical and horizontal scrollbars. Supports -all the same editing features as `wgtTextInput` plus multi-line selection -(Shift+Up/Down), Enter for newlines, and vertical/horizontal auto-scroll -during mouse drag selection. A horizontal scrollbar appears automatically -when any line exceeds the visible width. The vertical scrollbar is always -present. +Multi-line text editor with vertical and horizontal scrollbars. -### Dropdown and ComboBox +```c +WidgetT *ta = wgtTextArea(root, 4096); +ta->weight = 100; +wgtSetText(ta, "Line 1\nLine 2\nLine 3"); +``` + +Supports all TextInput editing features plus multi-line selection, +Enter for newlines, and vertical/horizontal auto-scroll. + +### ListBox + +```c +WidgetT *wgtListBox(WidgetT *parent); +void wgtListBoxSetItems(WidgetT *w, const char **items, int32_t count); +int32_t wgtListBoxGetSelected(const WidgetT *w); +void wgtListBoxSetSelected(WidgetT *w, int32_t idx); +``` + +```c +static const char *items[] = {"Alpha", "Beta", "Gamma", "Delta"}; + +WidgetT *lb = wgtListBox(root); +wgtListBoxSetItems(lb, items, 4); +wgtListBoxSetSelected(lb, 0); +lb->weight = 100; +lb->onClick = onItemSelected; +``` + +Multi-select mode: + +```c +wgtListBoxSetMultiSelect(lb, true); +wgtListBoxSetItemSelected(lb, 0, true); +wgtListBoxSetItemSelected(lb, 2, true); +bool sel = wgtListBoxIsItemSelected(lb, 1); +wgtListBoxSelectAll(lb); +wgtListBoxClearSelection(lb); +``` + +Reorderable (drag-and-drop items): + +```c +wgtListBoxSetReorderable(lb, true); +``` + +### Dropdown ```c WidgetT *wgtDropdown(WidgetT *parent); @@ -583,10 +876,20 @@ void wgtDropdownSetItems(WidgetT *w, const char **items, int32_t count); int32_t wgtDropdownGetSelected(const WidgetT *w); void wgtDropdownSetSelected(WidgetT *w, int32_t idx); ``` -Non-editable selection widget. Clicking the arrow button opens a popup -list below (or above if no room). Clicking the arrow again while the -popup is open closes it. Set `onChange` to be notified of selection -changes. The items array must remain valid for the widget's lifetime. +Non-editable selection widget with a popup list. + +```c +static const char *colors[] = {"Red", "Green", "Blue", "Yellow"}; + +WidgetT *dd = wgtDropdown(root); +wgtDropdownSetItems(dd, colors, 4); +wgtDropdownSetSelected(dd, 0); +dd->onChange = onColorChanged; +``` + +The items array must remain valid for the widget's lifetime. + +### ComboBox ```c WidgetT *wgtComboBox(WidgetT *parent, int32_t maxLen); @@ -594,30 +897,57 @@ void wgtComboBoxSetItems(WidgetT *w, const char **items, int32_t count); int32_t wgtComboBoxGetSelected(const WidgetT *w); void wgtComboBoxSetSelected(WidgetT *w, int32_t idx); ``` -Editable text field with a dropdown list. The text area accepts keyboard -input (same editing keys as `wgtTextInput`). Clicking the arrow button -toggles the popup; clicking the text area focuses it for editing. -Selecting an item copies its text into the edit buffer. Default weight -is 100. - -### ProgressBar and Slider +Editable text field with a dropdown list. ```c -WidgetT *wgtProgressBar(WidgetT *parent); +static const char *sizes[] = {"Small", "Medium", "Large", "Extra Large"}; + +WidgetT *cb = wgtComboBox(root, 32); +wgtComboBoxSetItems(cb, sizes, 4); +wgtComboBoxSetSelected(cb, 1); +cb->onChange = onSizeChanged; + +// Read typed/selected text: +const char *text = wgtGetText(cb); +``` + +### ProgressBar + +```c +WidgetT *wgtProgressBar(WidgetT *parent); // horizontal +WidgetT *wgtProgressBarV(WidgetT *parent); // vertical void wgtProgressBarSetValue(WidgetT *w, int32_t value); int32_t wgtProgressBarGetValue(const WidgetT *w); ``` -Horizontal progress indicator. Value is 0--100 (percentage). The filled -portion draws left-to-right with the system highlight color. + +```c +WidgetT *pb = wgtProgressBar(root); +pb->weight = 100; +wgtProgressBarSetValue(pb, 65); + +WidgetT *pbV = wgtProgressBarV(root); +wgtProgressBarSetValue(pbV, 75); +``` + +Value is 0--100 (percentage). + +### Slider ```c WidgetT *wgtSlider(WidgetT *parent, int32_t minVal, int32_t maxVal); void wgtSliderSetValue(WidgetT *w, int32_t value); int32_t wgtSliderGetValue(const WidgetT *w); ``` -Draggable slider/trackbar. Horizontal by default; set -`w->as.slider.vertical = true` after creation for vertical orientation. -The thumb tracks the mouse while held. Set `onChange` to be notified. + +```c +WidgetT *slider = wgtSlider(root, 0, 100); +wgtSliderSetValue(slider, 50); +slider->onChange = onVolumeChanged; + +// Vertical slider: +WidgetT *vSlider = wgtSlider(root, 0, 255); +vSlider->as.slider.vertical = true; +``` ### Spinner @@ -628,19 +958,17 @@ int32_t wgtSpinnerGetValue(const WidgetT *w); void wgtSpinnerSetRange(WidgetT *w, int32_t minVal, int32_t maxVal); void wgtSpinnerSetStep(WidgetT *w, int32_t step); ``` -Numeric up/down input field with increment/decrement buttons. The text -area accepts direct keyboard entry of digits (and minus sign when the -minimum is negative). Up/Down arrows increment/decrement by `step`; -Page Up/Page Down by `step * 10`. Enter commits the typed value; Escape -reverts. The value is clamped to `[minVal, maxVal]` on every change. +Numeric up/down input with increment/decrement buttons. -Supports all single-line text editing features: cursor movement, selection -(Shift+arrows, Ctrl+A, double/triple-click), clipboard (Ctrl+C/V/X), -and single-level undo (Ctrl+Z). Non-numeric input is filtered out. +```c +WidgetT *spin = wgtSpinner(root, 0, 999, 1); +wgtSpinnerSetValue(spin, 42); +spin->weight = 50; +spin->onChange = onQuantityChanged; +``` -The widget has a sunken text area with up/down arrow buttons on the right. -Clicking the top half of the button area increments; the bottom half -decrements. Set `onChange` to be notified of value changes. +Up/Down arrows increment/decrement by `step`; Page Up/Down by `step * 10`. +Enter commits the typed value; Escape reverts. ### TabControl @@ -650,21 +978,56 @@ WidgetT *wgtTabPage(WidgetT *parent, const char *title); void wgtTabControlSetActive(WidgetT *w, int32_t idx); int32_t wgtTabControlGetActive(const WidgetT *w); ``` -Tabbed container. Create a tab control, then add pages as children. -Each page is a VBox that holds its own widget subtree. Only the active -page is visible and receives layout; inactive pages are hidden. Clicking -a tab header switches the active page. -### StatusBar and Toolbar +```c +WidgetT *tabs = wgtTabControl(root); + +WidgetT *page1 = wgtTabPage(tabs, "&General"); +wgtLabel(page1, "General settings"); +wgtCheckbox(page1, "&Enable logging"); + +WidgetT *page2 = wgtTabPage(tabs, "&Advanced"); +wgtLabel(page2, "Advanced settings"); +WidgetT *slider = wgtSlider(page2, 0, 100); +wgtSliderSetValue(slider, 75); + +WidgetT *page3 = wgtTabPage(tabs, "A&bout"); +wgtLabel(page3, "Version 1.0"); +``` + +Each page is a VBox. Only the active page is visible and receives layout. + +### StatusBar ```c WidgetT *wgtStatusBar(WidgetT *parent); +``` +Horizontal container with sunken panel background. Place at the bottom of +the root VBox. + +```c +WidgetT *sb = wgtStatusBar(root); +WidgetT *msg = wgtLabel(sb, "Ready"); +msg->weight = 100; // stretch to fill +wgtLabel(sb, "Line 1, Col 1"); +``` + +### Toolbar + +```c WidgetT *wgtToolbar(WidgetT *parent); ``` -Horizontal containers with specialized chrome. StatusBar draws a sunken -panel background; Toolbar draws a raised background. Both use horizontal -layout with default spacing. Add child widgets (labels, buttons, etc.) -as with any HBox. StatusBar children with `weight > 0` stretch to fill. +Horizontal container with raised background. Place at the top of the root +VBox. + +```c +WidgetT *tb = wgtToolbar(root); +WidgetT *btnNew = wgtImageButton(tb, newPixels, 16, 16, 16 * bpp); +WidgetT *btnOpen = wgtImageButton(tb, openPixels, 16, 16, 16 * bpp); +WidgetT *btnSave = wgtImageButton(tb, savePixels, 16, 16, 16 * bpp); +wgtVSeparator(tb); +wgtButton(tb, "&Help"); +``` ### TreeView @@ -676,21 +1039,145 @@ bool wgtTreeItemIsExpanded(const WidgetT *w); WidgetT *wgtTreeViewGetSelected(const WidgetT *w); void wgtTreeViewSetSelected(WidgetT *w, WidgetT *item); ``` -Hierarchical tree with expand/collapse support. Create a tree view, then -add items as children. Nest items by adding them as children of other -items. Items with children show a `[+]`/`[-]` expand box. -The tree view manages its own internal scrollbars: a vertical scrollbar -appears when expanded items exceed the visible height, and a horizontal -scrollbar appears when item text extends beyond the visible width. Both -scrollbars have arrow buttons, proportional thumbs, and page -scrolling on trough clicks. +```c +WidgetT *tree = wgtTreeView(root); -Default weight is 100. Set `onClick` on individual tree items to handle -selection, or `onChange` to be notified of expand/collapse. +WidgetT *docs = wgtTreeItem(tree, "Documents"); +wgtTreeItemSetExpanded(docs, true); +wgtTreeItem(docs, "README.md"); +wgtTreeItem(docs, "DESIGN.md"); -`wgtTreeViewGetSelected` returns the currently selected tree item (or NULL). -`wgtTreeViewSetSelected` sets the selection programmatically. +WidgetT *src = wgtTreeItem(docs, "src"); +wgtTreeItemSetExpanded(src, true); +wgtTreeItem(src, "main.c"); +wgtTreeItem(src, "utils.c"); + +WidgetT *images = wgtTreeItem(tree, "Images"); +wgtTreeItem(images, "logo.png"); +wgtTreeItem(images, "icon.bmp"); +``` + +Multi-select and reorderable: + +```c +wgtTreeViewSetMultiSelect(tree, true); +wgtTreeViewSetReorderable(tree, true); +``` + +The tree view manages its own scrollbars automatically. Default weight is 100. + +### ListView + +```c +WidgetT *wgtListView(WidgetT *parent); +void wgtListViewSetColumns(WidgetT *w, const ListViewColT *cols, int32_t count); +void wgtListViewSetData(WidgetT *w, const char **cellData, int32_t rowCount); +int32_t wgtListViewGetSelected(const WidgetT *w); +void wgtListViewSetSelected(WidgetT *w, int32_t idx); +``` +Multi-column list with sortable headers and resizable columns. + +```c +static const ListViewColT cols[] = { + {"Name", wgtChars(16), ListViewAlignLeftE}, + {"Size", wgtChars(8), ListViewAlignRightE}, + {"Type", wgtChars(12), ListViewAlignLeftE}, + {"Modified", wgtChars(12), ListViewAlignLeftE} +}; + +static const char *data[] = { + "AUTOEXEC.BAT", "412", "Batch File", "03/15/1994", + "CONFIG.SYS", "256", "System File", "03/15/1994", + "COMMAND.COM", "54,645", "Application", "09/30/1993", +}; + +WidgetT *lv = wgtListView(root); +wgtListViewSetColumns(lv, cols, 4); +wgtListViewSetData(lv, data, 3); +wgtListViewSetSelected(lv, 0); +lv->weight = 100; +``` + +Multi-select and sorting: + +```c +wgtListViewSetMultiSelect(lv, true); +wgtListViewSetSort(lv, 0, ListViewSortAscE); +wgtListViewSetReorderable(lv, true); + +// Header click callback for custom sort logic: +wgtListViewSetHeaderClickCallback(lv, onHeaderClick); +``` + +Column widths support tagged sizes (`wgtPixels`, `wgtChars`, `wgtPercent`). +Cell data is a flat array of strings in row-major order. Both the column +and data arrays must remain valid for the widget's lifetime. + +### ScrollPane + +```c +WidgetT *wgtScrollPane(WidgetT *parent); +``` +Scrollable container. Children are laid out vertically at their natural +size. Scrollbars appear automatically when content exceeds the visible area. + +```c +WidgetT *sp = wgtScrollPane(root); +sp->weight = 100; +sp->padding = wgtPixels(4); +sp->spacing = wgtPixels(4); + +wgtLabel(sp, "Scrollable content:"); +wgtHSeparator(sp); +for (int32_t i = 0; i < 8; i++) { + wgtCheckbox(sp, items[i]); +} +wgtHSeparator(sp); +wgtButton(sp, "Scrolled Button"); +``` + +### Splitter + +```c +WidgetT *wgtSplitter(WidgetT *parent, bool vertical); +void wgtSplitterSetPos(WidgetT *w, int32_t pos); +int32_t wgtSplitterGetPos(const WidgetT *w); +``` +Resizable divider between exactly two child widgets. `vertical = true` +creates a left|right split; `false` creates a top/bottom split. + +```c +// Horizontal split: tree on left, list on right +WidgetT *vSplit = wgtSplitter(root, true); +vSplit->weight = 100; +wgtSplitterSetPos(vSplit, 150); + +WidgetT *leftTree = wgtTreeView(vSplit); +// ... populate tree ... + +WidgetT *rightList = wgtListBox(vSplit); +// ... populate list ... +``` + +Nested splitters for complex layouts: + +```c +// Outer: top/bottom split +WidgetT *hSplit = wgtSplitter(root, false); +hSplit->weight = 100; +wgtSplitterSetPos(hSplit, 120); + +// Top pane: left/right split +WidgetT *vSplit = wgtSplitter(hSplit, true); +wgtSplitterSetPos(vSplit, 120); +wgtTreeView(vSplit); // left +wgtListBox(vSplit); // right + +// Bottom pane: detail view +WidgetT *detail = wgtFrame(hSplit, "Preview"); +wgtLabel(detail, "Select a file above."); +``` ### Image @@ -701,10 +1188,15 @@ WidgetT *wgtImageFromFile(WidgetT *parent, const char *path); void wgtImageSetData(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH, int32_t pitch); ``` -Display a bitmap image. `wgtImage` takes ownership of the pixel buffer -(freed on destroy). `wgtImageFromFile` loads any format supported by -stb_image and converts to the display pixel format. Set `onClick` to -make the image clickable. + +```c +// From file (BMP, PNG, JPEG, GIF): +wgtImageFromFile(root, "sample.bmp"); + +// From pixel buffer (display format, takes ownership): +uint8_t *pixels = loadMyImage(&w, &h, &pitch); +wgtImage(root, pixels, w, h, pitch); +``` ### ImageButton @@ -714,65 +1206,93 @@ WidgetT *wgtImageButton(WidgetT *parent, uint8_t *data, void wgtImageButtonSetData(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH, int32_t pitch); ``` -Push button that displays an image instead of a text caption. Behaves -identically to `wgtButton` -- visually depresses on mouse-down, tracks the -cursor while held, and fires `onClick` on release if still over the button. -The image is centered on the button face with a 2px bevel border and no -extra padding. When pressed, the image shifts 1px down and right. +Push button that displays an image instead of text. Behaves identically +to `wgtButton`. -`wgtImageButton` takes ownership of the pixel buffer (freed on destroy). -`wgtImageButtonSetData` replaces the image data, freeing the previous buffer. -The pixel buffer must be in display pixel format (use `packColor()` and the -display's `bytesPerPixel` to prepare it). +```c +uint8_t *iconData = loadBmpPixels(&ctx, "new.bmp", &imgW, &imgH, &imgPitch); +WidgetT *btn = wgtImageButton(tb, iconData, imgW, imgH, imgPitch); +btn->onClick = onNewClick; +``` + +Takes ownership of the pixel buffer (freed on destroy). ### Canvas ```c WidgetT *wgtCanvas(WidgetT *parent, int32_t w, int32_t h); ``` -Drawable bitmap canvas with a sunken bevel border. Supports both -interactive freehand drawing (mouse strokes are Bresenham-interpolated) -and programmatic drawing from the API. +Drawable bitmap canvas with a sunken bevel border. Supports interactive +freehand drawing (mouse strokes) and programmatic drawing. ```c -void wgtCanvasClear(WidgetT *w, uint32_t color); -void wgtCanvasSetPenColor(WidgetT *w, uint32_t color); -void wgtCanvasSetPenSize(WidgetT *w, int32_t size); -int32_t wgtCanvasSave(WidgetT *w, const char *path); -int32_t wgtCanvasLoad(WidgetT *w, const char *path); -``` -`wgtCanvasSave` writes the canvas to a PNG file (converting from display -pixel format to RGB). `wgtCanvasLoad` reads any image format supported by -stb_image and converts it to display pixel format. +const DisplayT *d = dvxGetDisplay(&ctx); +WidgetT *cv = wgtCanvas(root, 280, 100); -Programmatic drawing primitives (all coordinates are in canvas space): +wgtCanvasClear(cv, packColor(d, 255, 255, 255)); -```c -void wgtCanvasSetPixel(WidgetT *w, int32_t x, int32_t y, uint32_t color); -uint32_t wgtCanvasGetPixel(const WidgetT *w, int32_t x, int32_t y); -void wgtCanvasDrawLine(WidgetT *w, int32_t x0, int32_t y0, int32_t x1, int32_t y1); -void wgtCanvasDrawRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height); -void wgtCanvasFillRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height); -void wgtCanvasFillCircle(WidgetT *w, int32_t cx, int32_t cy, int32_t radius); +wgtCanvasSetPenColor(cv, packColor(d, 200, 0, 0)); +wgtCanvasDrawRect(cv, 5, 5, 50, 35); + +wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 200)); +wgtCanvasFillCircle(cv, 150, 50, 25); + +wgtCanvasSetPenColor(cv, packColor(d, 0, 150, 0)); +wgtCanvasSetPenSize(cv, 3); +wgtCanvasDrawLine(cv, 70, 5, 130, 90); + +// Individual pixels: +wgtCanvasSetPixel(cv, 10, 10, packColor(d, 255, 0, 0)); +uint32_t px = wgtCanvasGetPixel(cv, 10, 10); + +// Save/load: +wgtCanvasSave(cv, "drawing.png"); +wgtCanvasLoad(cv, "image.png"); ``` -`SetPixel`/`GetPixel` take explicit colors. `DrawLine` uses the current -pen color and pen size. `DrawRect` draws a 1px outline, `FillRect` fills -solid, and `FillCircle` fills a circle -- all using the current pen -color. All operations clip to the canvas bounds. Colors are in display -pixel format (use `packColor()` to create them). + +Drawing primitives: `wgtCanvasDrawLine`, `wgtCanvasDrawRect` (outline), +`wgtCanvasFillRect` (solid), `wgtCanvasFillCircle`. All use the current +pen color and clip to the canvas bounds. ### ANSI Terminal ```c WidgetT *wgtAnsiTerm(WidgetT *parent, int32_t cols, int32_t rows); ``` -ANSI BBS terminal emulator widget. Displays a character grid (default -80x25 if cols/rows are 0) with full ANSI escape sequence support and a -16-color CGA palette. The terminal has a 2px sunken bevel border and a -vertical scrollbar for scrollback history. Supports mouse text selection -(click and drag), Ctrl+C to copy selected text (sends `^C` to the -terminal when nothing is selected), and Ctrl+V to paste clipboard text -through the comm interface. +ANSI BBS terminal emulator. Displays a character grid (default 80x25 if +cols/rows are 0) with full ANSI escape sequence support and a 16-color +CGA palette. + +```c +WidgetT *term = wgtAnsiTerm(root, 80, 25); +term->weight = 100; +wgtAnsiTermSetScrollback(term, 500); + +// Feed ANSI content directly: +static const uint8_t ansiData[] = + "\x1B[2J" // clear screen + "\x1B[1;34m=== Welcome ===\x1B[0m\r\n" + "\x1B[1mBold\x1B[0m, " + "\x1B[7mReverse\x1B[0m, " + "\x1B[5mBlinking\x1B[0m\r\n" + "\x1B[31m Red \x1B[32m Green \x1B[34m Blue \x1B[0m\r\n"; + +wgtAnsiTermWrite(term, ansiData, sizeof(ansiData) - 1); +``` + +Connect to a communications interface: + +```c +static int32_t myRead(void *ctx, uint8_t *buf, int32_t maxLen) { + return serialRead((SerialT *)ctx, buf, maxLen); +} + +static int32_t myWrite(void *ctx, const uint8_t *buf, int32_t len) { + return serialWrite((SerialT *)ctx, buf, len); +} + +wgtAnsiTermSetComm(term, &serial, myRead, myWrite); +``` ANSI escape sequences supported: @@ -789,77 +1309,35 @@ ANSI escape sequences supported: | `ESC[?7h/l` | Enable / disable auto-wrap | | `ESC[?25h/l` | Show / hide cursor | -Control characters: CR, LF, BS, TAB, BEL (ignored). - -```c -void wgtAnsiTermWrite(WidgetT *w, const uint8_t *data, int32_t len); -``` -Feed data through the ANSI parser. Use this to display `.ANS` files or -inject content without a communications link. - -```c -void wgtAnsiTermClear(WidgetT *w); -``` -Clear the screen, push all visible lines to scrollback, and reset the -cursor to the home position. - -```c -void wgtAnsiTermSetComm(WidgetT *w, void *ctx, - int32_t (*readFn)(void *, uint8_t *, int32_t), - int32_t (*writeFn)(void *, const uint8_t *, int32_t)); -``` -Set the communications interface. `readFn` should return bytes read -(0 if none available). `writeFn` sends bytes. Pass NULL function -pointers for a disconnected / display-only terminal. - -When connected, keyboard input is translated to ANSI sequences and sent -via `writeFn` (arrows become `ESC[A`..`ESC[D`, etc.). - -```c -int32_t wgtAnsiTermPoll(WidgetT *w); -``` -Poll the comm interface for incoming data and process it through the -ANSI parser. Also handles cursor blink and text blink timers. Returns -the number of bytes read. When using `dvxUpdate()`, terminal polling -is handled automatically -- call this manually only when running your -own event loop. - -```c -int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH); -``` -Fast repaint: renders only dirty rows directly into the window's content -buffer, bypassing the full widget paint pipeline (no clear, no relayout, -no other widgets). Returns the number of rows repainted (0 if nothing -was dirty). If `outY` and `outH` are non-NULL, they receive the -content-buffer-relative Y coordinate and height of the repainted region, -for targeted dirty-rect invalidation. Uses `drawTermRow()` internally -for efficient bulk rendering. When using `dvxUpdate()`, this is called -automatically after `wgtAnsiTermPoll()`. +Additional functions: ```c +void wgtAnsiTermClear(WidgetT *w); // clear + reset cursor +int32_t wgtAnsiTermPoll(WidgetT *w); // poll comm for data +int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *y, int32_t *h); // fast dirty-row repaint void wgtAnsiTermSetScrollback(WidgetT *w, int32_t maxLines); ``` -Set the scrollback buffer size (default 500 lines). Clears any existing -scrollback. Lines scroll into the buffer when they leave the top of the -screen or when the screen is cleared (`ESC[2J` / `wgtAnsiTermClear`). - -A vertical scrollbar appears automatically when there is scrollback -content. Click the arrow buttons for single-line scrolling, or the -trough for page scrolling. The view auto-follows live output unless the -user has scrolled back. ### Spacing and dividers ```c -WidgetT *wgtSpacer(WidgetT *parent); +WidgetT *wgtSpacer(WidgetT *parent); // flexible space (weight=100) +WidgetT *wgtHSeparator(WidgetT *parent); // horizontal line +WidgetT *wgtVSeparator(WidgetT *parent); // vertical line ``` -Invisible spacer. Default weight is 100 -- pushes siblings apart. ```c -WidgetT *wgtHSeparator(WidgetT *parent); // horizontal line -WidgetT *wgtVSeparator(WidgetT *parent); // vertical line +WidgetT *row = wgtHBox(root); +wgtButton(row, "Left"); +wgtSpacer(row); // pushes next button to the right +wgtButton(row, "Right"); +``` + +### Tooltips + +```c +wgtSetTooltip(widget, "Tooltip text appears on hover"); ``` -Visual divider lines (shadow + highlight, 2px). ### Size specifications @@ -903,6 +1381,13 @@ void wgtSetVisible(WidgetT *w, bool visible); Disabled widgets are drawn grayed out and ignore input. Hidden widgets are excluded from layout. +```c +// Disable an entire group of widgets: +wgtSetEnabled(btn, false); +wgtSetEnabled(slider, false); +wgtSetEnabled(textInput, false); +``` + ```c void wgtInvalidate(WidgetT *w); ``` @@ -910,10 +1395,25 @@ Trigger a full relayout and repaint of the widget tree. Call after modifying widget properties, adding/removing widgets, or changing text. ```c -WidgetT *wgtFind(WidgetT *root, const char *name); +void wgtInvalidatePaint(WidgetT *w); +``` +Lightweight repaint without relayout. Use when only visual state changed +(slider value, cursor blink, checkbox toggle) but widget sizes are stable. + +```c +WidgetT *wgtFind(WidgetT *root, const char *name); +void wgtSetName(WidgetT *w, const char *name); +``` +Search the subtree for a widget by name (up to 31 chars). Uses djb2 hash +for fast rejection. + +```c +wgtSetName(statusLabel, "status"); +// ... later, from any callback: +WidgetT *lbl = wgtFind(root, "status"); +wgtSetText(lbl, "Updated!"); +wgtInvalidate(lbl); ``` -Search the subtree for a widget by name. Set `w->name` (up to 31 chars) -to make a widget findable. ```c void wgtDestroy(WidgetT *w); @@ -921,7 +1421,7 @@ void wgtDestroy(WidgetT *w); Remove a widget and all its children from the tree and free memory. ```c -void wgtSetDebugLayout(bool enabled); +void wgtSetDebugLayout(AppContextT *ctx, bool enabled); ``` When enabled, draws 1px neon-colored borders around all layout containers so their bounds are visible. Each container gets a distinct color derived @@ -952,78 +1452,6 @@ void wgtPaint(WidgetT *root, DisplayT *d, const BlitOpsT *ops, ``` Paint the entire widget tree into the window's content buffer. -### List box operations - -```c -void wgtListBoxSetItems(WidgetT *w, const char **items, int32_t count); -int32_t wgtListBoxGetSelected(const WidgetT *w); -void wgtListBoxSetSelected(WidgetT *w, int32_t idx); -``` - -### ListView - -```c -WidgetT *wgtListView(WidgetT *parent); -void wgtListViewSetColumns(WidgetT *w, const ListViewColT *cols, int32_t count); -void wgtListViewSetData(WidgetT *w, const char **cellData, int32_t rowCount); -int32_t wgtListViewGetSelected(const WidgetT *w); -void wgtListViewSetSelected(WidgetT *w, int32_t idx); -``` -Multi-column list with sortable headers, resizable columns, and single or -multi-select support. Column definitions use `ListViewColT`: - -```c -typedef struct { - const char *title; - int32_t width; // tagged size (wgtPixels/wgtChars/wgtPercent) - ListViewAlignE align; // ListViewAlignLeftE, CenterE, or RightE -} ListViewColT; -``` - -Column widths support tagged sizes (`wgtPixels`, `wgtChars`, `wgtPercent`). -Cell data is a flat array of strings in row-major order (row0col0, row0col1, -..., row1col0, ...). Both the column and data arrays must remain valid for -the widget's lifetime. - -Clicking a column header sorts the list. An arrow indicator shows the -current sort direction. Column borders can be dragged to resize. Vertical -and horizontal scrollbars appear automatically when needed. - -```c -void wgtListViewSetSort(WidgetT *w, int32_t col, ListViewSortE dir); -void wgtListViewSetHeaderClickCallback(WidgetT *w, - void (*cb)(WidgetT *w, int32_t col, ListViewSortE dir)); -``` -Programmatic sort control. The header click callback fires after the sort -direction is updated, allowing custom sort logic. - -```c -void wgtListViewSetMultiSelect(WidgetT *w, bool multi); -bool wgtListViewIsItemSelected(const WidgetT *w, int32_t idx); -void wgtListViewSetItemSelected(WidgetT *w, int32_t idx, bool selected); -void wgtListViewSelectAll(WidgetT *w); -void wgtListViewClearSelection(WidgetT *w); -``` -Multi-select mode supports Ctrl+click to toggle individual items and -Shift+click for range selection. Default weight is 100. - -### ScrollPane - -```c -WidgetT *wgtScrollPane(WidgetT *parent); -``` -Scrollable container for child widgets. Children are laid out vertically -(like a VBox) at their natural size. Vertical and horizontal scrollbars -appear automatically when the content exceeds the visible area. - -Mouse clicks on the scrollbar arrows scroll by one line/character; clicks -on the trough scroll by one page. Clicks on the content area are forwarded -to child widgets. Keyboard scrolling: Up/Down arrows, Page Up/Down, -Home/End for vertical; Left/Right arrows for horizontal. - -Set `padding` and `spacing` to control internal layout. Default weight -is 100. - --- ## Types reference (`dvxTypes.h`) @@ -1104,6 +1532,40 @@ window's content buffer is used. Thumbnails are refreshed automatically when the window's content changes, with updates staggered across frames so only one icon redraws per interval. Double-click an icon to restore. +### Keyboard window management + +| Key | Action | +|-----|--------| +| Alt+Tab | Cycle windows forward | +| Shift+Alt+Tab | Cycle windows backward | +| Alt+F4 | Close focused window | +| Alt+Space | Open system menu | +| F10 | Activate menu bar | + +--- + +## Platform abstraction (`platform/dvxPlatform.h`) + +All OS-specific code is behind a single interface. To port DV/X, implement +these functions in a new `dvxPlatformXxx.c`: + +| Function | Purpose | +|----------|---------| +| `platformInit()` | One-time setup (signal handling, etc.) | +| `platformYield()` | Cooperative multitasking yield | +| `platformVideoInit()` | Set up video mode, LFB, backbuffer | +| `platformVideoShutdown()` | Restore text mode, free resources | +| `platformVideoSetPalette()` | Set 8-bit palette entries | +| `platformFlushRect()` | Copy rectangle from backbuffer to LFB | +| `platformSpanFill8/16/32()` | Optimized pixel fill | +| `platformSpanCopy8/16/32()` | Optimized pixel copy | +| `platformMouseInit()` | Initialize mouse driver | +| `platformMousePoll()` | Read mouse position and buttons | +| `platformKeyboardGetModifiers()` | Read shift/ctrl/alt state | +| `platformKeyboardRead()` | Read next key from buffer | +| `platformAltScanToChar()` | Map Alt+scancode to ASCII | +| `platformValidateFilename()` | Check filename for OS constraints | + --- ## Hardware requirements