Transparent image support. Image alignment in help.
This commit is contained in:
parent
3db721fdc0
commit
8abe947b8b
22 changed files with 462 additions and 21 deletions
4
.gitattributes
vendored
4
.gitattributes
vendored
|
|
@ -2,5 +2,9 @@
|
|||
*.BMP filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.JPG filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.PNG filter=lfs diff=lfs merge=lfs -text
|
||||
*.xcf filter=lfs diff=lfs merge=lfs -text
|
||||
*.XCF filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.ZIP filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
|
|||
4
Makefile
4
Makefile
|
|
@ -66,6 +66,7 @@ compile-help:
|
|||
apps/dvxhelp/help.dhs
|
||||
$(HLPC) -o bin/apps/kpunch/progman/dvxhelp.hlp \
|
||||
--html docs/dvx_system_reference.html \
|
||||
-i assets \
|
||||
$(SYSTEM_DHS) \
|
||||
$$(find widgets -name "*.dhs" ! -path "widgets/wgtsys.dhs" | sort)
|
||||
$(HLPC) -o bin/apps/kpunch/dvxbasic/dvxbasic.hlp \
|
||||
|
|
@ -105,6 +106,9 @@ deploy-sdk:
|
|||
cp "$$h" $(SDKDIR)/include/widget/"$$wgt"/; \
|
||||
done; \
|
||||
done
|
||||
@# BASIC include files
|
||||
@mkdir -p $(SDKDIR)/include/basic
|
||||
@cp sdk/include/basic/*.bas $(SDKDIR)/include/basic/ 2>/dev/null || true
|
||||
@# Samples and readme
|
||||
@cp -r sdk/samples $(SDKDIR)/
|
||||
@cp sdk/readme.txt $(SDKDIR)/README.TXT
|
||||
|
|
|
|||
|
|
@ -2579,3 +2579,136 @@ static BasValueT zeroValue(void) {
|
|||
memset(&v, 0, sizeof(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Common dialog wrappers for DECLARE LIBRARY "commdlg"
|
||||
// ============================================================
|
||||
//
|
||||
// Thin wrappers around dvx dialog functions. They retrieve the
|
||||
// AppContextT from the running form runtime internally, so BASIC
|
||||
// programs don't need to manage context pointers. Wrappers are
|
||||
// needed because: (1) dvx functions require AppContextT* as first
|
||||
// arg, and (2) functions with output buffers (file paths, text)
|
||||
// need C-side storage since BASIC strings can't be used as
|
||||
// writable char pointers.
|
||||
|
||||
const char *basFileOpen(const char *title, const char *filter) {
|
||||
if (!sFormRt) {
|
||||
return "";
|
||||
}
|
||||
|
||||
FileFilterT filters[2];
|
||||
filters[0].label = filter;
|
||||
filters[0].pattern = filter;
|
||||
filters[1].label = "All Files (*.*)";
|
||||
filters[1].pattern = "*.*";
|
||||
|
||||
static char path[260];
|
||||
path[0] = '\0';
|
||||
|
||||
if (dvxFileDialog(sFormRt->ctx, title, FD_OPEN, NULL, filters, 2, path, sizeof(path))) {
|
||||
return path;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
const char *basFileSave(const char *title, const char *filter) {
|
||||
if (!sFormRt) {
|
||||
return "";
|
||||
}
|
||||
|
||||
FileFilterT filters[2];
|
||||
filters[0].label = filter;
|
||||
filters[0].pattern = filter;
|
||||
filters[1].label = "All Files (*.*)";
|
||||
filters[1].pattern = "*.*";
|
||||
|
||||
static char path[260];
|
||||
path[0] = '\0';
|
||||
|
||||
if (dvxFileDialog(sFormRt->ctx, title, FD_SAVE, NULL, filters, 2, path, sizeof(path))) {
|
||||
return path;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
const char *basInputBox2(const char *title, const char *prompt, const char *defaultText) {
|
||||
if (!sFormRt) {
|
||||
return "";
|
||||
}
|
||||
|
||||
static char buf[512];
|
||||
buf[0] = '\0';
|
||||
|
||||
if (dvxInputBox(sFormRt->ctx, title, prompt, defaultText, buf, sizeof(buf))) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
int32_t basChoiceDialog(const char *title, const char *prompt, const char *items, int32_t defaultIdx) {
|
||||
if (!sFormRt || !items) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Split pipe-delimited items string into an array
|
||||
static char itemBuf[1024];
|
||||
snprintf(itemBuf, sizeof(itemBuf), "%s", items);
|
||||
|
||||
const char *ptrs[64];
|
||||
int32_t count = 0;
|
||||
char *tok = itemBuf;
|
||||
|
||||
while (*tok && count < 64) {
|
||||
ptrs[count++] = tok;
|
||||
char *sep = strchr(tok, '|');
|
||||
|
||||
if (sep) {
|
||||
*sep = '\0';
|
||||
tok = sep + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t chosen = 0;
|
||||
|
||||
if (dvxChoiceDialog(sFormRt->ctx, title, prompt, ptrs, count, defaultIdx, &chosen)) {
|
||||
return chosen;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int32_t basIntInput(const char *title, const char *prompt, int32_t defaultVal, int32_t minVal, int32_t maxVal) {
|
||||
if (!sFormRt) {
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
int32_t result = defaultVal;
|
||||
dvxIntInputBox(sFormRt->ctx, title, prompt, defaultVal, minVal, maxVal, 1, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int32_t basPromptSave(const char *title) {
|
||||
if (!sFormRt) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
return dvxPromptSave(sFormRt->ctx, title);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ static void printCallback(void *ctx, const char *text, bool newline);
|
|||
static bool inputCallback(void *ctx, const char *prompt, char *buf, int32_t bufSize);
|
||||
static bool doEventsCallback(void *ctx);
|
||||
static void *resolveExternCallback(void *ctx, const char *libName, const char *funcName);
|
||||
static BasValueT callExternCallback(void *ctx, void *funcPtr, const char *funcName, BasValueT *args, int32_t argc, uint8_t retType);
|
||||
static BasValueT callExternCallback(void *ctx, void *funcPtr, const char *libName, const char *funcName, BasValueT *args, int32_t argc, uint8_t retType);
|
||||
static void runCached(void);
|
||||
static void runModule(BasModuleT *mod);
|
||||
static void onEditorChange(WidgetT *w);
|
||||
|
|
@ -1328,6 +1328,31 @@ static int32_t localToConcatLine(int32_t editorLine) {
|
|||
// toggleBreakpointLine -- toggle breakpoint on a specific line
|
||||
// ============================================================
|
||||
|
||||
static void clearAllBreakpoints(void) {
|
||||
arrsetlen(sBreakpoints, 0);
|
||||
sBreakpointCount = 0;
|
||||
arrfree(sVmBreakpoints);
|
||||
sVmBreakpoints = NULL;
|
||||
updateBreakpointWindow();
|
||||
}
|
||||
|
||||
|
||||
static void removeBreakpointsForFile(int32_t fileIdx) {
|
||||
// Remove breakpoints for the given file and adjust indices for
|
||||
// files above the removed one (since file indices shift down).
|
||||
for (int32_t i = sBreakpointCount - 1; i >= 0; i--) {
|
||||
if (sBreakpoints[i].fileIdx == fileIdx) {
|
||||
arrdel(sBreakpoints, i);
|
||||
} else if (sBreakpoints[i].fileIdx > fileIdx) {
|
||||
sBreakpoints[i].fileIdx--;
|
||||
}
|
||||
}
|
||||
|
||||
sBreakpointCount = (int32_t)arrlen(sBreakpoints);
|
||||
updateBreakpointWindow();
|
||||
}
|
||||
|
||||
|
||||
static void toggleBreakpointLine(int32_t editorLine) {
|
||||
int32_t fileIdx = sProject.activeFileIdx;
|
||||
|
||||
|
|
@ -3357,6 +3382,7 @@ static void closeProject(void) {
|
|||
}
|
||||
|
||||
freeProcBufs();
|
||||
clearAllBreakpoints();
|
||||
|
||||
// Close project window
|
||||
prjClose(&sProject);
|
||||
|
|
@ -5357,6 +5383,7 @@ static void handleProjectCmd(int32_t cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
removeBreakpointsForFile(rmIdx);
|
||||
prjRemoveFile(&sProject, rmIdx);
|
||||
|
||||
if (sProject.activeFileIdx == rmIdx) {
|
||||
|
|
@ -5927,8 +5954,9 @@ static void *resolveExternCallback(void *ctx, const char *libName, const char *f
|
|||
//
|
||||
// Supported types: Integer (int16_t passed as int32_t), Long (int32_t),
|
||||
// Single/Double (double), String (const char *), Boolean (int32_t).
|
||||
static BasValueT callExternCallback(void *ctx, void *funcPtr, const char *funcName, BasValueT *args, int32_t argc, uint8_t retType) {
|
||||
static BasValueT callExternCallback(void *ctx, void *funcPtr, const char *libName, const char *funcName, BasValueT *args, int32_t argc, uint8_t retType) {
|
||||
(void)ctx;
|
||||
(void)libName;
|
||||
(void)funcName;
|
||||
|
||||
// Convert BASIC values to native C values and collect pointers
|
||||
|
|
|
|||
|
|
@ -3175,9 +3175,11 @@ BasVmResultE basVmStep(BasVmT *vm) {
|
|||
BasValueT result = basValInteger(0);
|
||||
|
||||
if (vm->ext.callExtern) {
|
||||
const char *libName = libNameIdx < (uint16_t)vm->module->constCount
|
||||
? vm->module->constants[libNameIdx]->data : "";
|
||||
const char *funcName = funcNameIdx < (uint16_t)vm->module->constCount
|
||||
? vm->module->constants[funcNameIdx]->data : "";
|
||||
result = vm->ext.callExtern(vm->ext.ctx, funcPtr, funcName, args, argCount, retType);
|
||||
result = vm->ext.callExtern(vm->ext.ctx, funcPtr, libName, funcName, args, argCount, retType);
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < argCount; i++) {
|
||||
|
|
|
|||
|
|
@ -187,8 +187,10 @@ typedef void *(*BasResolveExternFnT)(void *ctx, const char *libName, const char
|
|||
// Call a resolved native function. funcPtr is the pointer returned
|
||||
// by resolveExtern. args[0..argc-1] are the BASIC arguments.
|
||||
// retType is the expected return type (BAS_TYPE_*).
|
||||
// libName is the DECLARE LIBRARY name (e.g. "dvx"); the host may use
|
||||
// it to inject hidden parameters (e.g. AppContextT for "dvx" library).
|
||||
// Returns the native function's return value as a BasValueT.
|
||||
typedef BasValueT (*BasCallExternFnT)(void *ctx, void *funcPtr, const char *funcName, BasValueT *args, int32_t argc, uint8_t retType);
|
||||
typedef BasValueT (*BasCallExternFnT)(void *ctx, void *funcPtr, const char *libName, const char *funcName, BasValueT *args, int32_t argc, uint8_t retType);
|
||||
|
||||
typedef struct {
|
||||
BasResolveExternFnT resolveExtern;
|
||||
|
|
|
|||
|
|
@ -971,7 +971,10 @@ static void displayRecord(const HlpRecordHdrT *hdr, const char *payload) {
|
|||
const HlpImageRefT *imgRef = (const HlpImageRefT *)payload;
|
||||
uint32_t absOffset = sHeader.imagePoolOffset + imgRef->imageOffset;
|
||||
|
||||
// Read BMP data from file
|
||||
// Save file position — buildContentWidgets reads records
|
||||
// sequentially and we must not disturb its position.
|
||||
long savedPos = ftell(sHlpFile);
|
||||
|
||||
uint8_t *bmpData = (uint8_t *)dvxMalloc(imgRef->imageSize);
|
||||
|
||||
if (bmpData) {
|
||||
|
|
@ -982,16 +985,35 @@ static void displayRecord(const HlpRecordHdrT *hdr, const char *payload) {
|
|||
int32_t imgW = 0;
|
||||
int32_t imgH = 0;
|
||||
int32_t imgP = 0;
|
||||
uint8_t *pixels = dvxLoadImageFromMemory(sAc, bmpData, (int32_t)imgRef->imageSize, &imgW, &imgH, &imgP);
|
||||
bool hasAlpha = false;
|
||||
uint32_t keyColor = 0;
|
||||
uint8_t *pixels = dvxLoadImageAlpha(sAc, bmpData, (int32_t)imgRef->imageSize, &imgW, &imgH, &imgP, &hasAlpha, &keyColor);
|
||||
|
||||
if (pixels) {
|
||||
wgtImage(sContentBox, pixels, imgW, imgH, imgP);
|
||||
// wgtImage takes ownership of the pixel data
|
||||
WidgetT *parent = sContentBox;
|
||||
|
||||
if (hdr->flags == HLP_IMG_CENTER || hdr->flags == HLP_IMG_RIGHT) {
|
||||
WidgetT *row = wgtHBox(sContentBox);
|
||||
|
||||
if (row) {
|
||||
row->align = (hdr->flags == HLP_IMG_CENTER) ? AlignCenterE : AlignEndE;
|
||||
parent = row;
|
||||
}
|
||||
}
|
||||
|
||||
WidgetT *imgWgt = wgtImage(parent, pixels, imgW, imgH, imgP);
|
||||
|
||||
if (imgWgt && hasAlpha) {
|
||||
wgtImageSetTransparent(imgWgt, true, keyColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dvxFree(bmpData);
|
||||
}
|
||||
|
||||
// Restore file position for sequential record reading
|
||||
fseek(sHlpFile, savedPos, SEEK_SET);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -1134,12 +1156,8 @@ static void buildContentWidgets(void) {
|
|||
wgtScrollPaneScrollToTop(sContentScroll);
|
||||
}
|
||||
|
||||
// Two invalidate passes: the first does measure+layout which may change
|
||||
// scrollbar visibility (altering available width). The second re-measures
|
||||
// at the corrected width so heights match the actual layout.
|
||||
if (sContentScroll) {
|
||||
wgtInvalidate(sContentScroll);
|
||||
wgtInvalidate(sContentScroll);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@
|
|||
#define HLP_NOTE_TIP 0x01
|
||||
#define HLP_NOTE_WARNING 0x02
|
||||
|
||||
// Image alignment flags (HlpRecordHdrT.flags when type == HLP_REC_IMAGE)
|
||||
// 0x00 = left (default, backward compatible with older .hlp files)
|
||||
#define HLP_IMG_LEFT 0x00
|
||||
#define HLP_IMG_CENTER 0x01
|
||||
#define HLP_IMG_RIGHT 0x02
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TOC entry flags
|
||||
|
|
|
|||
BIN
assets/DVX Help Logo.xcf
(Stored with Git LFS)
Normal file
BIN
assets/DVX Help Logo.xcf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/DVX Logo.xcf
(Stored with Git LFS)
Normal file
BIN
assets/DVX Logo.xcf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/DVX Text.xcf
(Stored with Git LFS)
Normal file
BIN
assets/DVX Text.xcf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/help.png
(Stored with Git LFS)
Normal file
BIN
assets/help.png
(Stored with Git LFS)
Normal file
Binary file not shown.
|
|
@ -4491,6 +4491,73 @@ uint8_t *dvxLoadImageFromMemory(const AppContextT *ctx, const uint8_t *data, int
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxLoadImageAlpha
|
||||
// ============================================================
|
||||
|
||||
uint8_t *dvxLoadImageAlpha(const AppContextT *ctx, const uint8_t *data, int32_t dataLen, int32_t *outW, int32_t *outH, int32_t *outPitch, bool *outHasAlpha, uint32_t *outKeyColor) {
|
||||
if (!ctx || !data || dataLen <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const DisplayT *d = &ctx->display;
|
||||
|
||||
int imgW;
|
||||
int imgH;
|
||||
int channels;
|
||||
uint8_t *rgba = stbi_load_from_memory(data, dataLen, &imgW, &imgH, &channels, 4);
|
||||
|
||||
if (!rgba) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32_t bpp = d->format.bytesPerPixel;
|
||||
int32_t pitch = imgW * bpp;
|
||||
uint32_t keyColor = packColor(d, 255, 0, 255); // magenta
|
||||
bool hasAlpha = false;
|
||||
uint8_t *buf = (uint8_t *)malloc(pitch * imgH);
|
||||
|
||||
if (!buf) {
|
||||
stbi_image_free(rgba);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int32_t y = 0; y < imgH; y++) {
|
||||
for (int32_t x = 0; x < imgW; x++) {
|
||||
const uint8_t *src = rgba + (y * imgW + x) * 4;
|
||||
uint32_t color;
|
||||
|
||||
if (src[3] < 128) {
|
||||
color = keyColor;
|
||||
hasAlpha = true;
|
||||
} else {
|
||||
color = packColor(d, src[0], src[1], src[2]);
|
||||
}
|
||||
|
||||
uint8_t *dst = buf + y * pitch + x * bpp;
|
||||
|
||||
if (bpp == 1) {
|
||||
*dst = (uint8_t)color;
|
||||
} else if (bpp == 2) {
|
||||
*(uint16_t *)dst = (uint16_t)color;
|
||||
} else {
|
||||
*(uint32_t *)dst = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stbi_image_free(rgba);
|
||||
|
||||
if (outW) { *outW = imgW; }
|
||||
if (outH) { *outH = imgH; }
|
||||
if (outPitch) { *outPitch = pitch; }
|
||||
if (outHasAlpha) { *outHasAlpha = hasAlpha; }
|
||||
if (outKeyColor) { *outKeyColor = keyColor; }
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxLoadTheme
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -309,6 +309,12 @@ uint8_t *dvxLoadImage(const AppContextT *ctx, const char *path, int32_t *outW, i
|
|||
// format as dvxLoadImage. Caller must free the result with dvxFreeImage.
|
||||
uint8_t *dvxLoadImageFromMemory(const AppContextT *ctx, const uint8_t *data, int32_t dataLen, int32_t *outW, int32_t *outH, int32_t *outPitch);
|
||||
|
||||
// Load an image with alpha transparency support. Pixels with alpha < 128
|
||||
// are replaced with the key color (packed magenta). If any transparent
|
||||
// pixels were found, *outKeyColor receives the packed key color and
|
||||
// *outHasAlpha is set to true. Caller must free with dvxFreeImage.
|
||||
uint8_t *dvxLoadImageAlpha(const AppContextT *ctx, const uint8_t *data, int32_t dataLen, int32_t *outW, int32_t *outH, int32_t *outPitch, bool *outHasAlpha, uint32_t *outKeyColor);
|
||||
|
||||
// Free a pixel buffer returned by dvxLoadImage.
|
||||
void dvxFreeImage(uint8_t *data);
|
||||
|
||||
|
|
|
|||
|
|
@ -1348,6 +1348,73 @@ void rectCopyGrayscale(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t d
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// rectCopyTransparent
|
||||
// ============================================================
|
||||
//
|
||||
// Per-pixel color-key blit. Pixels matching keyColor are skipped,
|
||||
// leaving the destination unchanged (background shows through).
|
||||
// Used for PNG images with alpha transparency converted to key color.
|
||||
|
||||
void rectCopyTransparent(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t dstY, const uint8_t *srcBuf, int32_t srcPitch, int32_t srcX, int32_t srcY, int32_t w, int32_t h, uint32_t keyColor) {
|
||||
int32_t bpp = ops->bytesPerPixel;
|
||||
|
||||
int32_t origDstX = dstX;
|
||||
int32_t origDstY = dstY;
|
||||
|
||||
clipRect(d, &dstX, &dstY, &w, &h);
|
||||
|
||||
if (__builtin_expect(w <= 0 || h <= 0, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
srcX += dstX - origDstX;
|
||||
srcY += dstY - origDstY;
|
||||
|
||||
for (int32_t row = 0; row < h; row++) {
|
||||
const uint8_t *src = srcBuf + (srcY + row) * srcPitch + srcX * bpp;
|
||||
uint8_t *dst = d->backBuf + (dstY + row) * d->pitch + dstX * bpp;
|
||||
|
||||
if (bpp == 1) {
|
||||
uint8_t key8 = (uint8_t)keyColor;
|
||||
|
||||
for (int32_t col = 0; col < w; col++) {
|
||||
if (*src != key8) {
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
} else if (bpp == 2) {
|
||||
uint16_t key16 = (uint16_t)keyColor;
|
||||
|
||||
for (int32_t col = 0; col < w; col++) {
|
||||
uint16_t px = *(const uint16_t *)src;
|
||||
|
||||
if (px != key16) {
|
||||
*(uint16_t *)dst = px;
|
||||
}
|
||||
|
||||
src += 2;
|
||||
dst += 2;
|
||||
}
|
||||
} else {
|
||||
for (int32_t col = 0; col < w; col++) {
|
||||
uint32_t px = *(const uint32_t *)src;
|
||||
|
||||
if (px != keyColor) {
|
||||
*(uint32_t *)dst = px;
|
||||
}
|
||||
|
||||
src += 4;
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// rectFill
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ void rectCopy(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t dstY, cons
|
|||
// luminance (0.299R + 0.587G + 0.114B) for a disabled/grayed appearance.
|
||||
void rectCopyGrayscale(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t dstY, const uint8_t *srcBuf, int32_t srcPitch, int32_t srcX, int32_t srcY, int32_t w, int32_t h);
|
||||
|
||||
// Copy with color-key transparency. Pixels matching keyColor are skipped,
|
||||
// letting the destination (background) show through.
|
||||
void rectCopyTransparent(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t dstY, const uint8_t *srcBuf, int32_t srcPitch, int32_t srcX, int32_t srcY, int32_t w, int32_t h, uint32_t keyColor);
|
||||
|
||||
// Draw a beveled frame. The bevel is drawn as overlapping horizontal and
|
||||
// vertical spans -- top/left in highlight color, bottom/right in shadow.
|
||||
// The face color fills the interior if non-zero.
|
||||
|
|
|
|||
|
|
@ -5,4 +5,6 @@
|
|||
|
||||
.h1 Welcome!
|
||||
|
||||
.image help.png center
|
||||
|
||||
DVX (DOS Visual eXecutive) is a graphical user interface environment for DOS, designed for 486-class hardware and above. This help file covers the system architecture, core API, libraries, and widget toolkit.
|
||||
|
|
|
|||
|
|
@ -764,6 +764,7 @@ img { max-width: 100%; }
|
|||
<div class="topic" id="sys.welcome">
|
||||
<h1>Welcome!</h1>
|
||||
<h2>Welcome!</h2>
|
||||
<p><img src="data:image/bmp;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAB0CAYAAAD0DOulAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+oEDgIyCfl8ewwAAAyBSURBVHja7Z15jJ1VGcZ/352ZO53OdDojAwVEKAhlK5TFgmUJBMGwSCgJW4OINIZKKhqICwoxkIDIEgRNClGBoLK4gxKwGKBA2UIQEazI0kJVKC0tdoW20xn/OIO0w70zvdN7v/c953ue5P2ry33Pec77fO/Z3gPQL4vShPri14Zc9kbcb32G/XZHPRogMZEgClAC7jbk70ogi6i/MuAHhv111wBnEkCZBLGOsOTshoj66cZUxrfEIn2bB5wmbdssHGE8rTtQH4ohbQMwRQIoq9X6gK8BH5PGDYuJwHojnpYBoxz3TduYMWOsPhDrgN1TSvll+dtS4E5p3LB40JCjBUCTwz5pARYa9su9qa15yOzsNuBo6dyQeNaQnzedZetbA0sM++PpFBd9Zba2QRo3JJqA+Yb8HO+oL0427IeXqdOOrwRQton19PScKp0bEq3A84YcnemgD6Ybj9Mkd3NkvuwYaV1VfN2YG8uP1DTjtp8vAZTltUs8WVpXFTOwOx4zD2g2arfl8szZKTdQ5syyLNvQ09OzrbSuKj5vyM9cg/Y+leq0VwIoq2a3S+eqogdYbMjN7Bzb+rBhO98CxkgAZVZ2Pz7PoXnAaGC5ITdP5NBGy+M/75LjQfCSxrNQAccC96kbKmINMAF43+j39wV2beD/vxewh0XDsiyjq6vr3Lz7VhmPrJKtaXCgxY6bjfnZrwFtmmzcppuKtMsj82+9gDZFqiQshJJMlrv2O9exPbtiWwTiNozKgSnQZUPZ8q6urq2kd1XxI0Nu/lrHdrxk2I5rLQlUkMtUW3DkOMyYm6vr0IbrDf3fABwsAZR5PyT9TWldVew+sHBvxc91I5w+ZsAsQ7/fA3axJk8BLssz20gV+xhzU44w/vfyQJwCW1aLjZPWVc2mHjPk5W2guwZ/e4BFhv4+5IU0rfEIteAdYDywWl1RES8Cexv99mpgB+C/w/y9bkK9QavK08/TmGM8NUMHoYVa0QP8Xd1QFQcAzxn9djthN3eoWzwtwD+txK+zs/MZnBXd0LROpp3htOJq+hB+zdC4yZ8oIbGPU3t7e39nZ+cRoqgqzsH2YPHJFXw6HduTBNMkgEJKGfoG4HOiqCqmGvNzzgeOjB07dqaVH1mW9ZdKpROKHGBC2ssUenO4Op405OVhJ+Oo8MElpC2Cc7CrWOwdLYQjKlbcPEeoKG1Z28/t2MjrGEymOKibCHrOdA4RRRXRDfyLsEtbJKwAdiTUT3QJHYMR6oUpwB/VDRXxLrB/Adu9j2fx0xRYU+FG2G9EUVXcSnGOSd2kgJIAFlEAVwHbiaaquKYA4nd5LGRoDTBOAfSOXmASYfFd+CjWk+6m0VrsrtjVDK0BCo1AM7ouNxT2IJSCSg2rgd1iclgZoDLAPD6yWgb5KHYBXkusTeOBN2IbnILQSKiGYGXMB76VUHvOj038Ns4otAkivhppl4q2qnia+Dc9HldASQAlgEObbopURkvkIvhY7NxKAMVXHvY6Ee0OGuC+SHmNGloDFPLCToR7od3qior4eWwf4paWllOUUSgDVAaosVAvfBbbGoK11PY7UgGlQS8BHFl9uLNFY1Wcpo+YBFBIWAAH7ERRWXVZaqFj3haQ2LleCaD4sqomfYLorIh2QgUZb5wtBUan9rURBKuxd4G6oSJWEzaNFjry6TVCbb81EkBBqA8+AzzK0M84FhUr8FVf8R4SfQtaU2DxZWodHR1/E61VcbkDji5JeRoiCKZYtWrVPuqFqniEcOzECr2E2x7KKJQBKgNssEkIN8XhjrhJ8r0XlcOKUwBTxTpgApFWFUmc76eAQ42zUU2BhaRRLpfLr6gbAPits4/dp4E7NaXSFLgIfE0iHHewmm5dVXCO73C8THGrAkoCWAS+XjAOtJ8WdOnkAfyv1f5eASUBTJ2vEjDXgQgWBm1tbXuVSqUYNquWAFsroCSAReDrCcNAezelQBsGOxOuB8ayY7+OcDNEASUBTJqvo40DcwWJ3T8djHK5PA54n/iOLb0HdCmgJICp83W6caC9mbIINjU1LSDes5uPKqAkgMnzlWXZucaBliouIP4D7DMVUBLAovBlWT5rWmJcfps4KkBvToXoCxRQEsDU+erBvk7dVxLh8coEhG+wXaaAkgCmztcowgtvloE2NnIO24FXEhTAeUBbTEToLnCcAuiBL8sP28vARGB9hPy1EgqdbpPo+HwLGE84JuMeugssjBTfM/ztCcCTEfZZRrhhs03C42I74HlNqTQFLgJfs9DOcC04PsFpbzU7RgElAUydrwy4SyK4WTiMuG561GPX/iAFlAQwdb4mGgfaOmCyc85OKpDwDT4ec5z3NQltgsQngN74mgg8C5QN++VQwr3lonLmXWdcQpsgQj3wIrCbpQOtra1zu7u72531SzMwR8ODB3D88p+mwJoC14OvFuAh4ynXr5xx9WBBp76VbLYCSigCX382DrQbnfDk5Y7vRcB3nfjyZQWUkDpfGbDIONBmGXO0PeE5SesNiAs38uk7DgSwFxingBJS58v6vNvbQIcRPzsAKx2IzdoKvvU68Gu5NxGUAEoAG8HXmcaBtnIgE8sTE/FR3WUZlW+bbD8gQB4ywT09BJN2gYVG4XZsjz90AAty/s1p2B/5WEEoVb+4wp+9OfBnq4x9bAJOUUYhFIWvItwU+SE+Nhou3gxfr3Di67UKKCF1vlqB+caB1ugagpc5EZTbavDZy9vDlyighNT5asJ+R3R6owhpbm72ICT31Dj9zoD7Hfi9klAfUQElJM+XdbDVGyVCbT/rdt2yhWu11v6/brV2qk0QIU983/j3/1LHMd8xELifcNCvv9iCf/szB/7vBLyKUTVpZYDKAPPiKwOuc5Bt1CPQluHjrN+kOrRlMqGqjnV7FiughCLwFftU+DR81Pbbr46cfAof5wNPUkAJqfO1+0D2YhZspVLp6BH6Pg0fu6eXNoCXq5207RQFlJA6X+OwvzJ2QITZaz/wjwby4uW1uo8roITU+bI+i7Yuy7JDIhPAeYSnSRuFtoENCet2Pq6AEqyCL0/cbRloo0aN2tz2znaSGeVxXKTZSVvvlQAKAHR0dKT80NCjxoH2yyGEpUwote9BEGbmyMmFTto8h1BsVwKo7C/pl9asA+1PVfya4EQIzjDg5AtO2j5eAlhgZFlWhKcmpzoItKMG+bQfPspbnWc4/L7qoP29hFJjEkBlfknzdSyw3jjQtneW+S1p9BRwGLQCS530xSfrnlzkNOD1LObIhM8s6SxouyHU0xvrxJf3CcdBlhn70QP8B9tnTyEUbzihnryUchzUMmXNm5txvGz4+51OOHibcM94mQNO3iEUUn3H2I/jgKvq+R+qGILgLVtfR7gpsqDgPDzkQHAGC/IjDvz4BnCWsjNZ0plnd3f3EVmW9RWUg1lORTkDbnbQP4sIZxUlgLKkp94XFbD/b4ggO73RQT+9AYyuh6Jrl1bwNAUejCsIb9oWAfOAvSPx9VUasCtbI5YSNmi0BihsOcrlske3Lh5Y9ykCpkbk64kOfOgCTtUUWJb6zvNuGJfPysEOilCwD3HSd1/UFFhIcfq7MbYCXuPDM3opYQrwVKS+Hwk87MCPbQk71ZoCC1uE0U79Wkq4nZEaXiC8VRIrngRecuDH7JH8IwmgMBhrHPu2mPCKWSqYA+xLOPsYK9YCexIq5lhiEuHsZM3Q2pcstmWQ+xLo76cT/Hg+66Bff1frmo/WAIUPxkIsaCIULogV/cDBwDOJjaHDCbdFLMfSWsJNojc0BRZSFD8IL7LFXGDjrATFD+AxYLqxD62EM4r7awosS73owlH4eJ6yFptO+jjPSV/vKwGUJV11prOz88C2trZY+noqxcEZDvr7D8PNFLQGWNQ5b5bR39+fUp1G7+N4CeGa25KCDLFtCVf7uo39+DEwQ2uAwqZq8aH4bZNIk+Y59u3fhMKmSwo0xBYB2wFvGftxLnCNMkDhw69eqURfX19qVbrLhJsiOzjzqw8Yg+/zlY1EB7DSgR9thArbygCLPO0FUhQ/CIeJJzv068oCix/AqqEysBwxH2ivOBuSacMjIVzsqL9v0mf3/7jFAR8rCWuTEkAJX9I4D/vnLK9Dj4ENXm6b5SAWviQBlAAWATPU5y7hIR5mSgAlekXA/Qb9voY0q9bUC3sPbEZYx8eZH6SlCpZ0phjCphhLOIbRltPvrQd2JryhK1THjoQd+2ZDH5YC47QLHLfgbWzCR7F8IBvLYxe2n3D/VOI3PBYCPzH2YStg7v8AFSZzItvMm/sAAAAASUVORK5CYII=" alt="help.png"></p>
|
||||
<p>DVX (DOS Visual eXecutive) is a graphical user interface environment for DOS, designed for 486-class hardware and above. This help file covers the system architecture, core API, libraries, and widget toolkit.</p>
|
||||
</div>
|
||||
<div class="topic" id="arch.overview">
|
||||
|
|
|
|||
41
sdk/include/basic/commdlg.bas
Normal file
41
sdk/include/basic/commdlg.bas
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
' commdlg.bas -- Common Dialog Library for DVX BASIC
|
||||
'
|
||||
' Provides file dialogs, input dialogs, and other common UI dialogs.
|
||||
' Wrappers handle the AppContextT parameter internally so BASIC
|
||||
' programs just pass the visible arguments.
|
||||
'
|
||||
' Usage:
|
||||
' '$INCLUDE: 'commdlg.bas'
|
||||
'
|
||||
' DIM path AS STRING
|
||||
' path = basFileOpen("Open Image", "*.bmp")
|
||||
' IF path <> "" THEN
|
||||
' ' ... use the file ...
|
||||
' END IF
|
||||
|
||||
DECLARE LIBRARY "basrt"
|
||||
' Show a file Open dialog. Returns selected path, or "" if cancelled.
|
||||
' filter$ is a DOS wildcard (e.g. "*.bmp", "*.txt").
|
||||
DECLARE FUNCTION basFileOpen(BYVAL title AS STRING, BYVAL filter AS STRING) AS STRING
|
||||
|
||||
' Show a file Save dialog. Returns selected path, or "" if cancelled.
|
||||
DECLARE FUNCTION basFileSave(BYVAL title AS STRING, BYVAL filter AS STRING) AS STRING
|
||||
|
||||
' Show a modal text input box. Returns entered text, or "" if cancelled.
|
||||
DECLARE FUNCTION basInputBox2(BYVAL title AS STRING, BYVAL prompt AS STRING, BYVAL defaultText AS STRING) AS STRING
|
||||
|
||||
' Show a choice dialog with a listbox. items$ is pipe-delimited
|
||||
' (e.g. "Red|Green|Blue"). Returns chosen index (0-based), or -1.
|
||||
DECLARE FUNCTION basChoiceDialog(BYVAL title AS STRING, BYVAL prompt AS STRING, BYVAL items AS STRING, BYVAL defaultIdx AS INTEGER) AS INTEGER
|
||||
|
||||
' Show a modal integer input box with spinner. Returns the entered value.
|
||||
DECLARE FUNCTION basIntInput(BYVAL title AS STRING, BYVAL prompt AS STRING, BYVAL defaultVal AS INTEGER, BYVAL minVal AS INTEGER, BYVAL maxVal AS INTEGER) AS INTEGER
|
||||
|
||||
' Show a "Save changes?" prompt with Yes/No/Cancel buttons.
|
||||
DECLARE FUNCTION basPromptSave(BYVAL title AS STRING) AS INTEGER
|
||||
END DECLARE
|
||||
|
||||
' Return value constants for basPromptSave
|
||||
CONST DVX_SAVE_YES = 0
|
||||
CONST DVX_SAVE_NO = 1
|
||||
CONST DVX_SAVE_CANCEL = 2
|
||||
|
|
@ -533,16 +533,39 @@ static void parseDirective(const char *line, TopicT **curTopic, bool *inList, bo
|
|||
emitError(".image requires a filename");
|
||||
return;
|
||||
}
|
||||
// Trim trailing whitespace
|
||||
|
||||
// Parse: .image filename [left|center|right]
|
||||
char imgFile[260];
|
||||
snprintf(imgFile, sizeof(imgFile), "%s", rest);
|
||||
imgFile[sizeof(imgFile) - 1] = '\0';
|
||||
|
||||
uint8_t alignFlags = HLP_IMG_LEFT;
|
||||
|
||||
// Split off optional alignment keyword after filename
|
||||
char *space = strchr(imgFile, ' ');
|
||||
|
||||
if (space) {
|
||||
*space = '\0';
|
||||
char *align = space + 1;
|
||||
|
||||
while (*align == ' ') { align++; }
|
||||
|
||||
if (strcasecmp(align, "center") == 0) {
|
||||
alignFlags = HLP_IMG_CENTER;
|
||||
} else if (strcasecmp(align, "right") == 0) {
|
||||
alignFlags = HLP_IMG_RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// Trim trailing whitespace from filename
|
||||
int32_t len = strlen(imgFile);
|
||||
|
||||
while (len > 0 && isspace(imgFile[len - 1])) {
|
||||
imgFile[--len] = '\0';
|
||||
}
|
||||
|
||||
addImageRef(imgFile);
|
||||
addRecord(*curTopic, HLP_REC_IMAGE, 0, imgFile, strlen(imgFile));
|
||||
addRecord(*curTopic, HLP_REC_IMAGE, alignFlags, imgFile, strlen(imgFile));
|
||||
|
||||
} else if (strcmp(directive, "link") == 0) {
|
||||
// .link <topic-id> <display text>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ typedef struct {
|
|||
WidgetT *(*fromFile)(WidgetT *parent, const char *path);
|
||||
void (*setData)(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH, int32_t pitch);
|
||||
void (*loadFile)(WidgetT *w, const char *path);
|
||||
void (*setTransparent)(WidgetT *w, bool hasTransparency, uint32_t keyColor);
|
||||
} ImageApiT;
|
||||
|
||||
static inline const ImageApiT *dvxImageApi(void) {
|
||||
|
|
@ -21,5 +22,6 @@ static inline const ImageApiT *dvxImageApi(void) {
|
|||
#define wgtImageFromFile(parent, path) dvxImageApi()->fromFile(parent, path)
|
||||
#define wgtImageSetData(w, data, imgW, imgH, pitch) dvxImageApi()->setData(w, data, imgW, imgH, pitch)
|
||||
#define wgtImageLoadFile(w, path) dvxImageApi()->loadFile(w, path)
|
||||
#define wgtImageSetTransparent(w, has, key) dvxImageApi()->setTransparent(w, has, key)
|
||||
|
||||
#endif // IMAGE_H
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ typedef struct {
|
|||
int32_t imgH;
|
||||
int32_t imgPitch;
|
||||
bool pressed;
|
||||
bool hasTransparency;
|
||||
uint32_t keyColor;
|
||||
} ImageDataT;
|
||||
|
||||
|
||||
|
|
@ -109,9 +111,15 @@ void widgetImagePaint(WidgetT *w, DisplayT *disp, const BlitOpsT *ops, const Bit
|
|||
}
|
||||
|
||||
if (w->enabled) {
|
||||
if (d->hasTransparency) {
|
||||
rectCopyTransparent(disp, ops, dx, dy,
|
||||
d->pixelData, d->imgPitch,
|
||||
0, 0, imgW, imgH, d->keyColor);
|
||||
} else {
|
||||
rectCopy(disp, ops, dx, dy,
|
||||
d->pixelData, d->imgPitch,
|
||||
0, 0, imgW, imgH);
|
||||
}
|
||||
} else {
|
||||
if (!d->grayData) {
|
||||
int32_t bufSize = d->imgPitch * d->imgH;
|
||||
|
|
@ -227,6 +235,15 @@ void wgtImageSetData(WidgetT *w, uint8_t *pixelData, int32_t imgW, int32_t imgH,
|
|||
}
|
||||
|
||||
|
||||
void wgtImageSetTransparent(WidgetT *w, bool hasTransparency, uint32_t keyColor) {
|
||||
VALIDATE_WIDGET_VOID(w, sTypeId);
|
||||
ImageDataT *d = (ImageDataT *)w->data;
|
||||
d->hasTransparency = hasTransparency;
|
||||
d->keyColor = keyColor;
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// BASIC-facing accessors
|
||||
// ============================================================
|
||||
|
|
@ -281,11 +298,13 @@ static const struct {
|
|||
WidgetT *(*fromFile)(WidgetT *parent, const char *path);
|
||||
void (*setData)(WidgetT *w, uint8_t *pixelData, int32_t imgW, int32_t imgH, int32_t pitch);
|
||||
void (*loadFile)(WidgetT *w, const char *path);
|
||||
void (*setTransparent)(WidgetT *w, bool hasTransparency, uint32_t keyColor);
|
||||
} sApi = {
|
||||
.create = wgtImage,
|
||||
.fromFile = wgtImageFromFile,
|
||||
.setData = wgtImageSetData,
|
||||
.loadFile = wgtImageLoadFile
|
||||
.loadFile = wgtImageLoadFile,
|
||||
.setTransparent = wgtImageSetTransparent
|
||||
};
|
||||
|
||||
static const WgtPropDescT sProps[] = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue