More code cleanup.
This commit is contained in:
parent
78302c26d2
commit
1556ba889c
28 changed files with 459 additions and 449 deletions
27
Makefile
Normal file
27
Makefile
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# DVX GUI — Top-level Makefile
|
||||||
|
#
|
||||||
|
# Builds the full DVX stack: library, task switcher, shell, and apps.
|
||||||
|
|
||||||
|
.PHONY: all clean dvx tasks dvxshell apps
|
||||||
|
|
||||||
|
all: dvx tasks dvxshell apps
|
||||||
|
|
||||||
|
dvx:
|
||||||
|
$(MAKE) -C dvx
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
$(MAKE) -C tasks
|
||||||
|
|
||||||
|
dvxshell: dvx tasks
|
||||||
|
$(MAKE) -C dvxshell
|
||||||
|
|
||||||
|
apps: dvx tasks dvxshell
|
||||||
|
$(MAKE) -C apps
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(MAKE) -C dvx clean
|
||||||
|
$(MAKE) -C tasks clean
|
||||||
|
$(MAKE) -C dvxshell clean
|
||||||
|
$(MAKE) -C apps clean
|
||||||
|
-rmdir obj/dvx/widgets obj/dvx/platform obj/dvx obj/tasks obj/dvxshell obj/apps obj 2>/dev/null
|
||||||
|
-rmdir bin/apps/progman bin/apps/notepad bin/apps/clock bin/apps/dvxdemo bin/apps bin lib 2>/dev/null
|
||||||
|
|
@ -10,25 +10,32 @@ OBJDIR = ../obj/apps
|
||||||
BINDIR = ../bin/apps
|
BINDIR = ../bin/apps
|
||||||
|
|
||||||
# App definitions: each is a subdir with a single .c file
|
# App definitions: each is a subdir with a single .c file
|
||||||
APPS = progman notepad clock
|
APPS = progman notepad clock dvxdemo
|
||||||
|
|
||||||
.PHONY: all clean $(APPS)
|
.PHONY: all clean $(APPS)
|
||||||
|
|
||||||
all: $(APPS)
|
all: $(APPS)
|
||||||
|
|
||||||
progman: $(BINDIR)/progman.app
|
progman: $(BINDIR)/progman/progman.app
|
||||||
notepad: $(BINDIR)/notepad.app
|
notepad: $(BINDIR)/notepad/notepad.app
|
||||||
clock: $(BINDIR)/clock.app
|
clock: $(BINDIR)/clock/clock.app
|
||||||
|
dvxdemo: $(BINDIR)/dvxdemo/dvxdemo.app
|
||||||
|
|
||||||
$(BINDIR)/progman.app: $(OBJDIR)/progman.o | $(BINDIR)
|
$(BINDIR)/progman/progman.app: $(OBJDIR)/progman.o | $(BINDIR)/progman
|
||||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||||
|
|
||||||
$(BINDIR)/notepad.app: $(OBJDIR)/notepad.o | $(BINDIR)
|
$(BINDIR)/notepad/notepad.app: $(OBJDIR)/notepad.o | $(BINDIR)/notepad
|
||||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||||
|
|
||||||
$(BINDIR)/clock.app: $(OBJDIR)/clock.o | $(BINDIR)
|
$(BINDIR)/clock/clock.app: $(OBJDIR)/clock.o | $(BINDIR)/clock
|
||||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -E _appShutdown -U $<
|
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -E _appShutdown -U $<
|
||||||
|
|
||||||
|
DVXDEMO_BMPS = logo.bmp new.bmp open.bmp sample.bmp save.bmp
|
||||||
|
|
||||||
|
$(BINDIR)/dvxdemo/dvxdemo.app: $(OBJDIR)/dvxdemo.o $(addprefix dvxdemo/,$(DVXDEMO_BMPS)) | $(BINDIR)/dvxdemo
|
||||||
|
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||||
|
cp $(addprefix dvxdemo/,$(DVXDEMO_BMPS)) $(BINDIR)/dvxdemo/
|
||||||
|
|
||||||
$(OBJDIR)/progman.o: progman/progman.c | $(OBJDIR)
|
$(OBJDIR)/progman.o: progman/progman.c | $(OBJDIR)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
|
@ -38,16 +45,33 @@ $(OBJDIR)/notepad.o: notepad/notepad.c | $(OBJDIR)
|
||||||
$(OBJDIR)/clock.o: clock/clock.c | $(OBJDIR)
|
$(OBJDIR)/clock.o: clock/clock.c | $(OBJDIR)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c | $(OBJDIR)
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(OBJDIR):
|
$(OBJDIR):
|
||||||
mkdir -p $(OBJDIR)
|
mkdir -p $(OBJDIR)
|
||||||
|
|
||||||
$(BINDIR):
|
$(BINDIR)/progman:
|
||||||
mkdir -p $(BINDIR)
|
mkdir -p $(BINDIR)/progman
|
||||||
|
|
||||||
|
$(BINDIR)/notepad:
|
||||||
|
mkdir -p $(BINDIR)/notepad
|
||||||
|
|
||||||
|
$(BINDIR)/clock:
|
||||||
|
mkdir -p $(BINDIR)/clock
|
||||||
|
|
||||||
|
$(BINDIR)/dvxdemo:
|
||||||
|
mkdir -p $(BINDIR)/dvxdemo
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
$(OBJDIR)/progman.o: progman/progman.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h
|
$(OBJDIR)/progman.o: progman/progman.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h
|
||||||
$(OBJDIR)/notepad.o: notepad/notepad.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h
|
$(OBJDIR)/notepad.o: notepad/notepad.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h
|
||||||
$(OBJDIR)/clock.o: clock/clock.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h ../tasks/taskswitch.h
|
$(OBJDIR)/clock.o: clock/clock.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h ../tasks/taskswitch.h
|
||||||
|
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJDIR) $(BINDIR)
|
rm -f $(OBJDIR)/progman.o $(OBJDIR)/notepad.o $(OBJDIR)/clock.o $(OBJDIR)/dvxdemo.o
|
||||||
|
rm -f $(BINDIR)/progman/progman.app
|
||||||
|
rm -f $(BINDIR)/notepad/notepad.app
|
||||||
|
rm -f $(BINDIR)/clock/clock.app
|
||||||
|
rm -f $(BINDIR)/dvxdemo/dvxdemo.app $(addprefix $(BINDIR)/dvxdemo/,$(DVXDEMO_BMPS))
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
// demo.c — DVX GUI demonstration application
|
// dvxdemo.c — DVX GUI demonstration app (DXE version)
|
||||||
|
//
|
||||||
|
// Callback-only app that opens several windows showcasing the DVX
|
||||||
|
// widget system, paint callbacks, menus, accelerators, etc.
|
||||||
|
|
||||||
#include "dvxApp.h"
|
#include "dvxApp.h"
|
||||||
#include "dvxDialog.h"
|
#include "dvxDialog.h"
|
||||||
#include "dvxWidget.h"
|
#include "dvxWidget.h"
|
||||||
|
#include "dvxWm.h"
|
||||||
|
#include "shellApp.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "thirdparty/stb_image.h"
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Menu command IDs
|
// Menu command IDs
|
||||||
|
|
@ -49,7 +53,8 @@
|
||||||
// Prototypes
|
// Prototypes
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static uint8_t *loadBmpPixels(AppContextT *ctx, const char *path, int32_t *outW, int32_t *outH, int32_t *outPitch);
|
int32_t appMain(DxeAppContextT *ctx);
|
||||||
|
static void onCanvasDraw(WidgetT *w, int32_t cx, int32_t cy, bool drag);
|
||||||
static void onCloseCb(WindowT *win);
|
static void onCloseCb(WindowT *win);
|
||||||
static void onCloseMainCb(WindowT *win);
|
static void onCloseMainCb(WindowT *win);
|
||||||
static void onMenuCb(WindowT *win, int32_t menuId);
|
static void onMenuCb(WindowT *win, int32_t menuId);
|
||||||
|
|
@ -58,102 +63,58 @@ static void onPaintColor(WindowT *win, RectT *dirtyArea);
|
||||||
static void onPaintPattern(WindowT *win, RectT *dirtyArea);
|
static void onPaintPattern(WindowT *win, RectT *dirtyArea);
|
||||||
static void onPaintText(WindowT *win, RectT *dirtyArea);
|
static void onPaintText(WindowT *win, RectT *dirtyArea);
|
||||||
static void onToolbarClick(WidgetT *w);
|
static void onToolbarClick(WidgetT *w);
|
||||||
static void setupControlsWindow(AppContextT *ctx);
|
static void setupControlsWindow(void);
|
||||||
static void setupMainWindow(AppContextT *ctx);
|
static void setupMainWindow(void);
|
||||||
static void setupTerminalWindow(AppContextT *ctx);
|
static void setupTerminalWindow(void);
|
||||||
static void setupWidgetDemo(AppContextT *ctx);
|
static void setupWidgetDemo(void);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Globals
|
// Module state
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static AppContextT *sCtx = NULL;
|
static AppContextT *sAc = NULL;
|
||||||
|
static DxeAppContextT *sDxeCtx = NULL;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// loadBmpPixels — load a BMP/PNG file into display-format pixels
|
// App descriptor
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static uint8_t *loadBmpPixels(AppContextT *ctx, const char *path, int32_t *outW, int32_t *outH, int32_t *outPitch) {
|
AppDescriptorT appDescriptor = {
|
||||||
int imgW;
|
.name = "DVX Demo",
|
||||||
int imgH;
|
.hasMainLoop = false,
|
||||||
int channels;
|
.stackSize = 0,
|
||||||
uint8_t *rgb = stbi_load(path, &imgW, &imgH, &channels, 3);
|
.priority = TS_PRIORITY_NORMAL
|
||||||
|
};
|
||||||
|
|
||||||
if (!rgb) {
|
// ============================================================
|
||||||
return NULL;
|
// Callbacks
|
||||||
}
|
// ============================================================
|
||||||
|
|
||||||
const DisplayT *d = dvxGetDisplay(ctx);
|
static void onCanvasDraw(WidgetT *w, int32_t cx, int32_t cy, bool drag) {
|
||||||
int32_t bpp = d->format.bytesPerPixel;
|
if (drag && w->as.canvas.lastX >= 0) {
|
||||||
int32_t pitch = imgW * bpp;
|
wgtCanvasDrawLine(w, w->as.canvas.lastX, w->as.canvas.lastY, cx, cy);
|
||||||
uint8_t *data = (uint8_t *)malloc(pitch * imgH);
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
stbi_image_free(rgb);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int32_t y = 0; y < imgH; y++) {
|
|
||||||
for (int32_t x = 0; x < imgW; x++) {
|
|
||||||
uint8_t *src = rgb + (y * imgW + x) * 3;
|
|
||||||
uint32_t color = packColor(d, src[0], src[1], src[2]);
|
|
||||||
uint8_t *dst = data + y * pitch + x * bpp;
|
|
||||||
|
|
||||||
if (bpp == 1) {
|
|
||||||
*dst = (uint8_t)color;
|
|
||||||
} else if (bpp == 2) {
|
|
||||||
*(uint16_t *)dst = (uint16_t)color;
|
|
||||||
} else {
|
} else {
|
||||||
*(uint32_t *)dst = color;
|
wgtCanvasDrawLine(w, cx, cy, cx, cy);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stbi_image_free(rgb);
|
|
||||||
*outW = imgW;
|
|
||||||
*outH = imgH;
|
|
||||||
*outPitch = pitch;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// onCloseCb
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
static void onCloseCb(WindowT *win) {
|
static void onCloseCb(WindowT *win) {
|
||||||
AppContextT *ctx = (AppContextT *)win->userData;
|
dvxDestroyWindow(sAc, win);
|
||||||
|
|
||||||
if (ctx) {
|
|
||||||
dvxDestroyWindow(ctx, win);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// onCloseMainCb
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
static void onCloseMainCb(WindowT *win) {
|
static void onCloseMainCb(WindowT *win) {
|
||||||
AppContextT *ctx = (AppContextT *)win->userData;
|
int32_t result = dvxMessageBox(sAc, "Close",
|
||||||
|
"Close this window?",
|
||||||
if (ctx) {
|
|
||||||
int32_t result = dvxMessageBox(ctx, "Exit",
|
|
||||||
"Are you sure you want to exit?",
|
|
||||||
MB_YESNO | MB_ICONQUESTION);
|
MB_YESNO | MB_ICONQUESTION);
|
||||||
|
|
||||||
if (result == ID_YES) {
|
if (result == ID_YES) {
|
||||||
dvxQuit(ctx);
|
dvxDestroyWindow(sAc, win);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// onMenuCb
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
static const FileFilterT sFileFilters[] = {
|
static const FileFilterT sFileFilters[] = {
|
||||||
{"All Files (*.*)", "*.*"},
|
{"All Files (*.*)", "*.*"},
|
||||||
{"Text Files (*.txt)", "*.txt"},
|
{"Text Files (*.txt)", "*.txt"},
|
||||||
|
|
@ -163,16 +124,14 @@ static const FileFilterT sFileFilters[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void onMenuCb(WindowT *win, int32_t menuId) {
|
static void onMenuCb(WindowT *win, int32_t menuId) {
|
||||||
AppContextT *ctx = (AppContextT *)win->userData;
|
|
||||||
|
|
||||||
switch (menuId) {
|
switch (menuId) {
|
||||||
case CMD_FILE_OPEN: {
|
case CMD_FILE_OPEN: {
|
||||||
char path[260];
|
char path[260];
|
||||||
|
|
||||||
if (dvxFileDialog(ctx, "Open File", FD_OPEN, NULL, sFileFilters, 5, path, sizeof(path))) {
|
if (dvxFileDialog(sAc, "Open File", FD_OPEN, NULL, sFileFilters, 5, path, sizeof(path))) {
|
||||||
char msg[300];
|
char msg[300];
|
||||||
snprintf(msg, sizeof(msg), "Selected: %s", path);
|
snprintf(msg, sizeof(msg), "Selected: %s", path);
|
||||||
dvxMessageBox(ctx, "Open", msg, MB_OK | MB_ICONINFO);
|
dvxMessageBox(sAc, "Open", msg, MB_OK | MB_ICONINFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -181,96 +140,84 @@ static void onMenuCb(WindowT *win, int32_t menuId) {
|
||||||
case CMD_FILE_SAVE: {
|
case CMD_FILE_SAVE: {
|
||||||
char path[260];
|
char path[260];
|
||||||
|
|
||||||
if (dvxFileDialog(ctx, "Save As", FD_SAVE, NULL, sFileFilters, 5, path, sizeof(path))) {
|
if (dvxFileDialog(sAc, "Save As", FD_SAVE, NULL, sFileFilters, 5, path, sizeof(path))) {
|
||||||
char msg[300];
|
char msg[300];
|
||||||
snprintf(msg, sizeof(msg), "Save to: %s", path);
|
snprintf(msg, sizeof(msg), "Save to: %s", path);
|
||||||
dvxMessageBox(ctx, "Save", msg, MB_OK | MB_ICONINFO);
|
dvxMessageBox(sAc, "Save", msg, MB_OK | MB_ICONINFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CMD_FILE_EXIT:
|
case CMD_FILE_EXIT:
|
||||||
if (ctx) {
|
onCloseMainCb(win);
|
||||||
int32_t result = dvxMessageBox(ctx, "Exit",
|
|
||||||
"Are you sure you want to exit?",
|
|
||||||
MB_YESNO | MB_ICONQUESTION);
|
|
||||||
|
|
||||||
if (result == ID_YES) {
|
|
||||||
dvxQuit(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_VIEW_TERM:
|
case CMD_VIEW_TERM:
|
||||||
setupTerminalWindow(ctx);
|
setupTerminalWindow();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_VIEW_CTRL:
|
case CMD_VIEW_CTRL:
|
||||||
setupControlsWindow(ctx);
|
setupControlsWindow();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_VIEW_DEBUG_LYT: {
|
case CMD_VIEW_DEBUG_LYT: {
|
||||||
static bool sDebugLyt = false;
|
static bool sDebugLyt = false;
|
||||||
sDebugLyt = !sDebugLyt;
|
sDebugLyt = !sDebugLyt;
|
||||||
wgtSetDebugLayout(ctx, sDebugLyt);
|
wgtSetDebugLayout(sAc, sDebugLyt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CMD_WIN_CASCADE:
|
case CMD_WIN_CASCADE:
|
||||||
dvxCascadeWindows(ctx);
|
dvxCascadeWindows(sAc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_WIN_TILE_H:
|
case CMD_WIN_TILE_H:
|
||||||
dvxTileWindowsH(ctx);
|
dvxTileWindowsH(sAc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_WIN_TILE_V:
|
case CMD_WIN_TILE_V:
|
||||||
dvxTileWindowsV(ctx);
|
dvxTileWindowsV(sAc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_WIN_TILE:
|
case CMD_WIN_TILE:
|
||||||
dvxTileWindows(ctx);
|
dvxTileWindows(sAc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_HELP_ABOUT:
|
case CMD_HELP_ABOUT:
|
||||||
dvxMessageBox(sCtx, "About DVX Demo",
|
dvxMessageBox(sAc, "About DVX Demo",
|
||||||
"DVX GUI Demonstration\n\n"
|
"DVX GUI Demonstration\n\n"
|
||||||
"A DOS Visual eXecutive windowing system for DOS.",
|
"A DOS Visual eXecutive windowing system for DOS.",
|
||||||
MB_OK | MB_ICONINFO);
|
MB_OK | MB_ICONINFO);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_CTX_CUT:
|
case CMD_CTX_CUT:
|
||||||
dvxMessageBox(ctx, "Context Menu", "Cut selected.", MB_OK | MB_ICONINFO);
|
dvxMessageBox(sAc, "Context Menu", "Cut selected.", MB_OK | MB_ICONINFO);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_CTX_COPY:
|
case CMD_CTX_COPY:
|
||||||
dvxMessageBox(ctx, "Context Menu", "Copied to clipboard.", MB_OK | MB_ICONINFO);
|
dvxMessageBox(sAc, "Context Menu", "Copied to clipboard.", MB_OK | MB_ICONINFO);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_CTX_PASTE:
|
case CMD_CTX_PASTE:
|
||||||
dvxMessageBox(ctx, "Context Menu", "Pasted from clipboard.", MB_OK | MB_ICONINFO);
|
dvxMessageBox(sAc, "Context Menu", "Pasted from clipboard.", MB_OK | MB_ICONINFO);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_CTX_DELETE:
|
case CMD_CTX_DELETE:
|
||||||
dvxMessageBox(ctx, "Context Menu", "Deleted.", MB_OK | MB_ICONINFO);
|
dvxMessageBox(sAc, "Context Menu", "Deleted.", MB_OK | MB_ICONINFO);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_CTX_SELALL:
|
case CMD_CTX_SELALL:
|
||||||
dvxMessageBox(ctx, "Context Menu", "Selected all.", MB_OK | MB_ICONINFO);
|
dvxMessageBox(sAc, "Context Menu", "Selected all.", MB_OK | MB_ICONINFO);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_CTX_PROPS:
|
case CMD_CTX_PROPS:
|
||||||
dvxMessageBox(ctx, "Properties", "Window properties dialog would appear here.", MB_OK | MB_ICONINFO);
|
dvxMessageBox(sAc, "Properties", "Window properties dialog would appear here.", MB_OK | MB_ICONINFO);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// onOkClick
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
static void onOkClick(WidgetT *w) {
|
static void onOkClick(WidgetT *w) {
|
||||||
WidgetT *root = w;
|
WidgetT *root = w;
|
||||||
|
|
||||||
|
|
@ -286,20 +233,15 @@ static void onOkClick(WidgetT *w) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// onPaintColor
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
static void onPaintColor(WindowT *win, RectT *dirtyArea) {
|
static void onPaintColor(WindowT *win, RectT *dirtyArea) {
|
||||||
(void)dirtyArea;
|
(void)dirtyArea;
|
||||||
AppContextT *ctx = (AppContextT *)win->userData;
|
|
||||||
|
|
||||||
if (!win->contentBuf || !ctx) {
|
if (!win->contentBuf || !sAc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DisplayT *d = dvxGetDisplay(ctx);
|
const DisplayT *d = dvxGetDisplay(sAc);
|
||||||
const BlitOpsT *ops = dvxGetBlitOps(ctx);
|
const BlitOpsT *ops = dvxGetBlitOps(sAc);
|
||||||
|
|
||||||
for (int32_t y = 0; y < win->contentH; y++) {
|
for (int32_t y = 0; y < win->contentH; y++) {
|
||||||
uint8_t r = (uint8_t)((y * 255) / (win->contentH > 1 ? win->contentH - 1 : 1));
|
uint8_t r = (uint8_t)((y * 255) / (win->contentH > 1 ? win->contentH - 1 : 1));
|
||||||
|
|
@ -314,19 +256,14 @@ static void onPaintColor(WindowT *win, RectT *dirtyArea) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// onPaintPattern
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
static void onPaintPattern(WindowT *win, RectT *dirtyArea) {
|
static void onPaintPattern(WindowT *win, RectT *dirtyArea) {
|
||||||
(void)dirtyArea;
|
(void)dirtyArea;
|
||||||
AppContextT *ctx = (AppContextT *)win->userData;
|
|
||||||
|
|
||||||
if (!win->contentBuf || !ctx) {
|
if (!win->contentBuf || !sAc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DisplayT *d = dvxGetDisplay(ctx);
|
const DisplayT *d = dvxGetDisplay(sAc);
|
||||||
int32_t bpp = d->format.bytesPerPixel;
|
int32_t bpp = d->format.bytesPerPixel;
|
||||||
int32_t sq = 16;
|
int32_t sq = 16;
|
||||||
|
|
||||||
|
|
@ -350,21 +287,16 @@ static void onPaintPattern(WindowT *win, RectT *dirtyArea) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// onPaintText
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
||||||
(void)dirtyArea;
|
(void)dirtyArea;
|
||||||
AppContextT *ctx = (AppContextT *)win->userData;
|
|
||||||
|
|
||||||
if (!win->contentBuf || !ctx) {
|
if (!win->contentBuf || !sAc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DisplayT *d = dvxGetDisplay(ctx);
|
const DisplayT *d = dvxGetDisplay(sAc);
|
||||||
const BlitOpsT *ops = dvxGetBlitOps(ctx);
|
const BlitOpsT *ops = dvxGetBlitOps(sAc);
|
||||||
const BitmapFontT *font = dvxGetFont(ctx);
|
const BitmapFontT *font = dvxGetFont(sAc);
|
||||||
int32_t bpp = d->format.bytesPerPixel;
|
int32_t bpp = d->format.bytesPerPixel;
|
||||||
|
|
||||||
uint32_t bg = packColor(d, 255, 255, 255);
|
uint32_t bg = packColor(d, 255, 255, 255);
|
||||||
|
|
@ -447,10 +379,6 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// onToolbarClick
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
static void onToolbarClick(WidgetT *w) {
|
static void onToolbarClick(WidgetT *w) {
|
||||||
WidgetT *root = w;
|
WidgetT *root = w;
|
||||||
|
|
||||||
|
|
@ -473,17 +401,16 @@ static void onToolbarClick(WidgetT *w) {
|
||||||
static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"};
|
static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"};
|
||||||
static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"};
|
static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"};
|
||||||
|
|
||||||
static void setupControlsWindow(AppContextT *ctx) {
|
static void setupControlsWindow(void) {
|
||||||
WindowT *win = dvxCreateWindow(ctx, "Advanced Widgets", 380, 50, 360, 440, true);
|
WindowT *win = dvxCreateWindow(sAc, "Advanced Widgets", 380, 50, 360, 440, true);
|
||||||
|
|
||||||
if (!win) {
|
if (!win) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
win->userData = ctx;
|
|
||||||
win->onClose = onCloseCb;
|
win->onClose = onCloseCb;
|
||||||
|
|
||||||
WidgetT *root = wgtInitWindow(ctx, win);
|
WidgetT *root = wgtInitWindow(sAc, win);
|
||||||
|
|
||||||
// TabControl at top
|
// TabControl at top
|
||||||
WidgetT *tabs = wgtTabControl(root);
|
WidgetT *tabs = wgtTabControl(root);
|
||||||
|
|
@ -617,58 +544,35 @@ static void setupControlsWindow(AppContextT *ctx) {
|
||||||
wgtLabel(sp, "Bottom of scroll area");
|
wgtLabel(sp, "Bottom of scroll area");
|
||||||
wgtButton(sp, "Scrolled &Button");
|
wgtButton(sp, "Scrolled &Button");
|
||||||
|
|
||||||
// --- Tab 5: Toolbar (ImageButtons + VSeparator) ---
|
// --- Tab 5: Toolbar (text buttons + VSeparator) ---
|
||||||
WidgetT *page5tb = wgtTabPage(tabs, "Tool&bar");
|
WidgetT *page5tb = wgtTabPage(tabs, "Tool&bar");
|
||||||
|
|
||||||
WidgetT *tb = wgtToolbar(page5tb);
|
WidgetT *tb = wgtToolbar(page5tb);
|
||||||
|
|
||||||
int32_t imgW;
|
WidgetT *btnNew = wgtButton(tb, "&New");
|
||||||
int32_t imgH;
|
|
||||||
int32_t imgPitch;
|
|
||||||
|
|
||||||
uint8_t *newData = loadBmpPixels(ctx, "new.bmp", &imgW, &imgH, &imgPitch);
|
|
||||||
if (newData) {
|
|
||||||
WidgetT *btnNew = wgtImageButton(tb, newData, imgW, imgH, imgPitch);
|
|
||||||
wgtSetName(btnNew, "New");
|
|
||||||
btnNew->onClick = onToolbarClick;
|
btnNew->onClick = onToolbarClick;
|
||||||
}
|
WidgetT *btnOpen = wgtButton(tb, "&Open");
|
||||||
|
|
||||||
uint8_t *openData = loadBmpPixels(ctx, "open.bmp", &imgW, &imgH, &imgPitch);
|
|
||||||
if (openData) {
|
|
||||||
WidgetT *btnOpen = wgtImageButton(tb, openData, imgW, imgH, imgPitch);
|
|
||||||
wgtSetName(btnOpen, "Open");
|
|
||||||
btnOpen->onClick = onToolbarClick;
|
btnOpen->onClick = onToolbarClick;
|
||||||
}
|
WidgetT *btnSave = wgtButton(tb, "&Save");
|
||||||
|
|
||||||
uint8_t *saveData = loadBmpPixels(ctx, "save.bmp", &imgW, &imgH, &imgPitch);
|
|
||||||
if (saveData) {
|
|
||||||
WidgetT *btnSave = wgtImageButton(tb, saveData, imgW, imgH, imgPitch);
|
|
||||||
wgtSetName(btnSave, "Save");
|
|
||||||
btnSave->onClick = onToolbarClick;
|
btnSave->onClick = onToolbarClick;
|
||||||
}
|
|
||||||
|
|
||||||
wgtVSeparator(tb);
|
wgtVSeparator(tb);
|
||||||
WidgetT *btnHelp = wgtButton(tb, "&Help");
|
WidgetT *btnHelp = wgtButton(tb, "&Help");
|
||||||
btnHelp->onClick = onToolbarClick;
|
btnHelp->onClick = onToolbarClick;
|
||||||
|
|
||||||
wgtLabel(page5tb, "ImageButtons with VSeparator.");
|
wgtLabel(page5tb, "Toolbar with text buttons and VSeparator.");
|
||||||
|
|
||||||
// --- Tab 6: Media (Image, ImageFromFile) ---
|
// --- Tab 6: Media (Image from file) ---
|
||||||
WidgetT *page6m = wgtTabPage(tabs, "&Media");
|
WidgetT *page6m = wgtTabPage(tabs, "&Media");
|
||||||
|
|
||||||
|
char samplePath[272];
|
||||||
|
snprintf(samplePath, sizeof(samplePath), "%s/sample.bmp", sDxeCtx->appDir);
|
||||||
wgtLabel(page6m, "ImageFromFile (sample.bmp):");
|
wgtLabel(page6m, "ImageFromFile (sample.bmp):");
|
||||||
wgtImageFromFile(page6m, "sample.bmp");
|
WidgetT *img = wgtImageFromFile(page6m, samplePath);
|
||||||
|
|
||||||
wgtHSeparator(page6m);
|
if (!img) {
|
||||||
|
wgtLabel(page6m, "(File not found)");
|
||||||
wgtLabel(page6m, "Image (logo.bmp):");
|
|
||||||
WidgetT *imgRow = wgtHBox(page6m);
|
|
||||||
uint8_t *logoData = loadBmpPixels(ctx, "logo.bmp", &imgW, &imgH, &imgPitch);
|
|
||||||
if (logoData) {
|
|
||||||
wgtImage(imgRow, logoData, imgW, imgH, imgPitch);
|
|
||||||
}
|
}
|
||||||
wgtVSeparator(imgRow);
|
|
||||||
wgtLabel(imgRow, "32x32 DVX logo");
|
|
||||||
|
|
||||||
// --- Tab 7: Editor (TextArea, Canvas) ---
|
// --- Tab 7: Editor (TextArea, Canvas) ---
|
||||||
WidgetT *page7e = wgtTabPage(tabs, "&Editor");
|
WidgetT *page7e = wgtTabPage(tabs, "&Editor");
|
||||||
|
|
@ -681,7 +585,7 @@ static void setupControlsWindow(AppContextT *ctx) {
|
||||||
wgtHSeparator(page7e);
|
wgtHSeparator(page7e);
|
||||||
|
|
||||||
wgtLabel(page7e, "Canvas (draw with mouse):");
|
wgtLabel(page7e, "Canvas (draw with mouse):");
|
||||||
const DisplayT *d = dvxGetDisplay(ctx);
|
const DisplayT *d = dvxGetDisplay(sAc);
|
||||||
WidgetT *cv = wgtCanvas(page7e, 280, 80);
|
WidgetT *cv = wgtCanvas(page7e, 280, 80);
|
||||||
wgtCanvasSetPenColor(cv, packColor(d, 200, 0, 0));
|
wgtCanvasSetPenColor(cv, packColor(d, 200, 0, 0));
|
||||||
wgtCanvasDrawRect(cv, 5, 5, 50, 35);
|
wgtCanvasDrawRect(cv, 5, 5, 50, 35);
|
||||||
|
|
@ -690,6 +594,7 @@ static void setupControlsWindow(AppContextT *ctx) {
|
||||||
wgtCanvasSetPenColor(cv, packColor(d, 0, 150, 0));
|
wgtCanvasSetPenColor(cv, packColor(d, 0, 150, 0));
|
||||||
wgtCanvasDrawLine(cv, 70, 5, 130, 70);
|
wgtCanvasDrawLine(cv, 70, 5, 130, 70);
|
||||||
wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 0));
|
wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 0));
|
||||||
|
wgtCanvasSetMouseCallback(cv, onCanvasDraw);
|
||||||
|
|
||||||
// --- Tab 8: Splitter ---
|
// --- Tab 8: Splitter ---
|
||||||
WidgetT *page8s = wgtTabPage(tabs, "S&plit");
|
WidgetT *page8s = wgtTabPage(tabs, "S&plit");
|
||||||
|
|
@ -831,12 +736,11 @@ static void setupControlsWindow(AppContextT *ctx) {
|
||||||
// setupMainWindow — info window + paint demos
|
// setupMainWindow — info window + paint demos
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void setupMainWindow(AppContextT *ctx) {
|
static void setupMainWindow(void) {
|
||||||
// Window 1: Text information window with menu bar
|
// Window 1: Text information window with menu bar
|
||||||
WindowT *win1 = dvxCreateWindow(ctx, "DVX Information", 50, 40, 340, 350, true);
|
WindowT *win1 = dvxCreateWindow(sAc, "DVX Information", 50, 40, 340, 350, true);
|
||||||
|
|
||||||
if (win1) {
|
if (win1) {
|
||||||
win1->userData = ctx;
|
|
||||||
win1->onPaint = onPaintText;
|
win1->onPaint = onPaintText;
|
||||||
win1->onClose = onCloseMainCb;
|
win1->onClose = onCloseMainCb;
|
||||||
win1->onMenu = onMenuCb;
|
win1->onMenu = onMenuCb;
|
||||||
|
|
@ -924,17 +828,16 @@ static void setupMainWindow(AppContextT *ctx) {
|
||||||
win1->contextMenu = winCtx;
|
win1->contextMenu = winCtx;
|
||||||
|
|
||||||
wmUpdateContentRect(win1);
|
wmUpdateContentRect(win1);
|
||||||
wmReallocContentBuf(win1, &ctx->display);
|
wmReallocContentBuf(win1, &sAc->display);
|
||||||
|
|
||||||
RectT fullRect = {0, 0, win1->contentW, win1->contentH};
|
RectT fullRect = {0, 0, win1->contentW, win1->contentH};
|
||||||
win1->onPaint(win1, &fullRect);
|
win1->onPaint(win1, &fullRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Window 2: Color gradient
|
// Window 2: Color gradient
|
||||||
WindowT *win2 = dvxCreateWindow(ctx, "Color Gradient", 200, 100, 280, 250, true);
|
WindowT *win2 = dvxCreateWindow(sAc, "Color Gradient", 200, 100, 280, 250, true);
|
||||||
|
|
||||||
if (win2) {
|
if (win2) {
|
||||||
win2->userData = ctx;
|
|
||||||
win2->onPaint = onPaintColor;
|
win2->onPaint = onPaintColor;
|
||||||
win2->onClose = onCloseCb;
|
win2->onClose = onCloseCb;
|
||||||
|
|
||||||
|
|
@ -943,17 +846,16 @@ static void setupMainWindow(AppContextT *ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Window 3: Checkerboard pattern with scrollbars
|
// Window 3: Checkerboard pattern with scrollbars
|
||||||
WindowT *win3 = dvxCreateWindow(ctx, "Pattern", 400, 150, 250, 220, true);
|
WindowT *win3 = dvxCreateWindow(sAc, "Pattern", 400, 150, 250, 220, true);
|
||||||
|
|
||||||
if (win3) {
|
if (win3) {
|
||||||
win3->userData = ctx;
|
|
||||||
win3->onPaint = onPaintPattern;
|
win3->onPaint = onPaintPattern;
|
||||||
win3->onClose = onCloseCb;
|
win3->onClose = onCloseCb;
|
||||||
|
|
||||||
wmAddVScrollbar(win3, 0, 100, 25);
|
wmAddVScrollbar(win3, 0, 100, 25);
|
||||||
wmAddHScrollbar(win3, 0, 100, 25);
|
wmAddHScrollbar(win3, 0, 100, 25);
|
||||||
wmUpdateContentRect(win3);
|
wmUpdateContentRect(win3);
|
||||||
wmReallocContentBuf(win3, &ctx->display);
|
wmReallocContentBuf(win3, &sAc->display);
|
||||||
|
|
||||||
RectT fullRect = {0, 0, win3->contentW, win3->contentH};
|
RectT fullRect = {0, 0, win3->contentW, win3->contentH};
|
||||||
win3->onPaint(win3, &fullRect);
|
win3->onPaint(win3, &fullRect);
|
||||||
|
|
@ -965,17 +867,16 @@ static void setupMainWindow(AppContextT *ctx) {
|
||||||
// setupTerminalWindow — ANSI terminal widget demo
|
// setupTerminalWindow — ANSI terminal widget demo
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void setupTerminalWindow(AppContextT *ctx) {
|
static void setupTerminalWindow(void) {
|
||||||
WindowT *win = dvxCreateWindow(ctx, "ANSI Terminal", 60, 60, 660, 420, true);
|
WindowT *win = dvxCreateWindow(sAc, "ANSI Terminal", 60, 60, 660, 420, true);
|
||||||
|
|
||||||
if (!win) {
|
if (!win) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
win->userData = ctx;
|
|
||||||
win->onClose = onCloseCb;
|
win->onClose = onCloseCb;
|
||||||
|
|
||||||
WidgetT *root = wgtInitWindow(ctx, win);
|
WidgetT *root = wgtInitWindow(sAc, win);
|
||||||
WidgetT *term = wgtAnsiTerm(root, 80, 25);
|
WidgetT *term = wgtAnsiTerm(root, 80, 25);
|
||||||
|
|
||||||
term->weight = 100;
|
term->weight = 100;
|
||||||
|
|
@ -1036,8 +937,8 @@ static void setupTerminalWindow(AppContextT *ctx) {
|
||||||
WidgetT *sb = wgtStatusBar(root);
|
WidgetT *sb = wgtStatusBar(root);
|
||||||
wgtLabel(sb, "80x25 [Local]");
|
wgtLabel(sb, "80x25 [Local]");
|
||||||
|
|
||||||
dvxFitWindow(ctx, win);
|
dvxFitWindow(sAc, win);
|
||||||
dvxMinimizeWindow(ctx, win);
|
dvxMinimizeWindow(sAc, win);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1045,18 +946,17 @@ static void setupTerminalWindow(AppContextT *ctx) {
|
||||||
// setupWidgetDemo — form with accelerators
|
// setupWidgetDemo — form with accelerators
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void setupWidgetDemo(AppContextT *ctx) {
|
static void setupWidgetDemo(void) {
|
||||||
WindowT *win = dvxCreateWindow(ctx, "Widget Demo", 80, 200, 280, 360, true);
|
WindowT *win = dvxCreateWindow(sAc, "Widget Demo", 80, 200, 280, 360, true);
|
||||||
|
|
||||||
if (!win) {
|
if (!win) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
win->userData = ctx;
|
|
||||||
win->onClose = onCloseCb;
|
win->onClose = onCloseCb;
|
||||||
win->onMenu = onMenuCb;
|
win->onMenu = onMenuCb;
|
||||||
|
|
||||||
WidgetT *root = wgtInitWindow(ctx, win);
|
WidgetT *root = wgtInitWindow(sAc, win);
|
||||||
|
|
||||||
// Status label at top
|
// Status label at top
|
||||||
WidgetT *status = wgtLabel(root, "Ready.");
|
WidgetT *status = wgtLabel(root, "Ready.");
|
||||||
|
|
@ -1134,48 +1034,17 @@ static void setupWidgetDemo(AppContextT *ctx) {
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// main
|
// Entry point
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int32_t appMain(DxeAppContextT *ctx) {
|
||||||
(void)argc;
|
sDxeCtx = ctx;
|
||||||
|
sAc = ctx->shellCtx;
|
||||||
|
|
||||||
// Change to executable's directory so relative BMP paths work
|
setupMainWindow();
|
||||||
char exeDir[260];
|
setupWidgetDemo();
|
||||||
strncpy(exeDir, argv[0], sizeof(exeDir) - 1);
|
setupControlsWindow();
|
||||||
exeDir[sizeof(exeDir) - 1] = '\0';
|
setupTerminalWindow();
|
||||||
char *lastSep = strrchr(exeDir, '/');
|
|
||||||
char *lastBs = strrchr(exeDir, '\\');
|
|
||||||
if (lastBs > lastSep) {
|
|
||||||
lastSep = lastBs;
|
|
||||||
}
|
|
||||||
if (lastSep) {
|
|
||||||
*lastSep = '\0';
|
|
||||||
chdir(exeDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppContextT ctx;
|
|
||||||
|
|
||||||
printf("DVX GUI Demo\n");
|
|
||||||
printf("Initializing VESA video...\n");
|
|
||||||
|
|
||||||
if (dvxInit(&ctx, 1024, 768, 16) != 0) {
|
|
||||||
fprintf(stderr, "Failed to initialize DVX GUI\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sCtx = &ctx;
|
|
||||||
|
|
||||||
setupMainWindow(&ctx);
|
|
||||||
setupWidgetDemo(&ctx);
|
|
||||||
setupControlsWindow(&ctx);
|
|
||||||
setupTerminalWindow(&ctx);
|
|
||||||
|
|
||||||
dvxRun(&ctx);
|
|
||||||
|
|
||||||
dvxShutdown(&ctx);
|
|
||||||
|
|
||||||
printf("DVX GUI Demo ended.\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
#define CMD_TILE 201
|
#define CMD_TILE 201
|
||||||
#define CMD_TILE_H 202
|
#define CMD_TILE_H 202
|
||||||
#define CMD_TILE_V 203
|
#define CMD_TILE_V 203
|
||||||
|
#define CMD_MIN_ON_RUN 104
|
||||||
#define CMD_ABOUT 300
|
#define CMD_ABOUT 300
|
||||||
#define CMD_TASK_MGR 301
|
#define CMD_TASK_MGR 301
|
||||||
|
|
||||||
|
|
@ -52,6 +53,7 @@ static AppContextT *sAc = NULL;
|
||||||
static int32_t sMyAppId = 0;
|
static int32_t sMyAppId = 0;
|
||||||
static WindowT *sPmWindow = NULL;
|
static WindowT *sPmWindow = NULL;
|
||||||
static WidgetT *sStatusLabel = NULL;
|
static WidgetT *sStatusLabel = NULL;
|
||||||
|
static bool sMinOnRun = false;
|
||||||
static AppEntryT sAppFiles[MAX_APP_FILES];
|
static AppEntryT sAppFiles[MAX_APP_FILES];
|
||||||
static int32_t sAppCount = 0;
|
static int32_t sAppCount = 0;
|
||||||
|
|
||||||
|
|
@ -117,6 +119,9 @@ static void buildPmWindow(void) {
|
||||||
wmAddMenuSeparator(fileMenu);
|
wmAddMenuSeparator(fileMenu);
|
||||||
wmAddMenuItem(fileMenu, "E&xit Shell", CMD_EXIT);
|
wmAddMenuItem(fileMenu, "E&xit Shell", CMD_EXIT);
|
||||||
|
|
||||||
|
MenuT *optMenu = wmAddMenu(menuBar, "&Options");
|
||||||
|
wmAddMenuCheckItem(optMenu, "&Minimize on Run", CMD_MIN_ON_RUN, false);
|
||||||
|
|
||||||
MenuT *windowMenu = wmAddMenu(menuBar, "&Window");
|
MenuT *windowMenu = wmAddMenu(menuBar, "&Window");
|
||||||
wmAddMenuItem(windowMenu, "&Cascade", CMD_CASCADE);
|
wmAddMenuItem(windowMenu, "&Cascade", CMD_CASCADE);
|
||||||
wmAddMenuItem(windowMenu, "&Tile", CMD_TILE);
|
wmAddMenuItem(windowMenu, "&Tile", CMD_TILE);
|
||||||
|
|
@ -242,6 +247,10 @@ static void onAppButtonClick(WidgetT *w) {
|
||||||
|
|
||||||
shellLoadApp(sAc, entry->path);
|
shellLoadApp(sAc, entry->path);
|
||||||
updateStatusText();
|
updateStatusText();
|
||||||
|
|
||||||
|
if (sMinOnRun && sPmWindow) {
|
||||||
|
dvxMinimizeWindow(sAc, sPmWindow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -271,6 +280,10 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
|
||||||
if (dvxFileDialog(sAc, "Run Application", FD_OPEN, "apps", filters, 2, path, sizeof(path))) {
|
if (dvxFileDialog(sAc, "Run Application", FD_OPEN, "apps", filters, 2, path, sizeof(path))) {
|
||||||
shellLoadApp(sAc, path);
|
shellLoadApp(sAc, path);
|
||||||
updateStatusText();
|
updateStatusText();
|
||||||
|
|
||||||
|
if (sMinOnRun && sPmWindow) {
|
||||||
|
dvxMinimizeWindow(sAc, sPmWindow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -295,6 +308,10 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
|
||||||
dvxTileWindowsV(sAc);
|
dvxTileWindowsV(sAc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CMD_MIN_ON_RUN:
|
||||||
|
sMinOnRun = !sMinOnRun;
|
||||||
|
break;
|
||||||
|
|
||||||
case CMD_ABOUT:
|
case CMD_ABOUT:
|
||||||
showAboutDialog();
|
showAboutDialog();
|
||||||
break;
|
break;
|
||||||
|
|
@ -485,7 +502,7 @@ static void scanAppsDir(void) {
|
||||||
|
|
||||||
static void showAboutDialog(void) {
|
static void showAboutDialog(void) {
|
||||||
dvxMessageBox(sAc, "About DVX Shell",
|
dvxMessageBox(sAc, "About DVX Shell",
|
||||||
"DVX Shell 1.0\n\nA DOS Visual eXecutive desktop shell for DJGPP/DPMI. Using DXE3 dynamic loading for application modules.",
|
"DVX Shell 1.0\nA DOS Visual eXecutive desktop shell for DJGPP/DPMI. Using DXE3 dynamic loading for application modules.",
|
||||||
MB_OK | MB_ICONINFO);
|
MB_OK | MB_ICONINFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,4 +130,4 @@ $(WOBJDIR)/widgetScrollbar.o: widgets/widgetScrollbar.c $(WIDGET_DEPS)
|
||||||
$(WOBJDIR)/widgetTreeView.o: widgets/widgetTreeView.c $(WIDGET_DEPS)
|
$(WOBJDIR)/widgetTreeView.o: widgets/widgetTreeView.c $(WIDGET_DEPS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJDIR) $(LIBDIR)
|
rm -f $(OBJS) $(POBJS) $(WOBJS) $(TARGET)
|
||||||
|
|
|
||||||
58
dvx/dvxApp.c
58
dvx/dvxApp.c
|
|
@ -19,8 +19,12 @@
|
||||||
#define KB_MOVE_STEP 8
|
#define KB_MOVE_STEP 8
|
||||||
#define MENU_CHECK_WIDTH 14
|
#define MENU_CHECK_WIDTH 14
|
||||||
#define SUBMENU_ARROW_WIDTH 12
|
#define SUBMENU_ARROW_WIDTH 12
|
||||||
|
#define SUBMENU_ARROW_HALF 3 // half-size of submenu arrow glyph
|
||||||
#define TOOLTIP_DELAY_MS 500
|
#define TOOLTIP_DELAY_MS 500
|
||||||
#define TOOLTIP_PAD 3
|
#define TOOLTIP_PAD 3
|
||||||
|
#define POPUP_BEVEL_WIDTH 2 // popup menu border bevel thickness
|
||||||
|
#define POPUP_ITEM_PAD_H 8 // extra horizontal padding in popup items
|
||||||
|
#define MENU_TAB_GAP_CHARS 3 // char-widths gap between label and shortcut
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Prototypes
|
// Prototypes
|
||||||
|
|
@ -133,7 +137,7 @@ static void calcPopupSize(const AppContextT *ctx, const MenuT *menu, int32_t *pw
|
||||||
memcpy(leftBuf, label, leftLen);
|
memcpy(leftBuf, label, leftLen);
|
||||||
leftBuf[leftLen] = '\0';
|
leftBuf[leftLen] = '\0';
|
||||||
|
|
||||||
itemW = textWidthAccel(&ctx->font, leftBuf) + ctx->font.charWidth * 3 + textWidth(&ctx->font, tab + 1);
|
itemW = textWidthAccel(&ctx->font, leftBuf) + ctx->font.charWidth * MENU_TAB_GAP_CHARS + textWidth(&ctx->font, tab + 1);
|
||||||
} else {
|
} else {
|
||||||
itemW = textWidthAccel(&ctx->font, label);
|
itemW = textWidthAccel(&ctx->font, label);
|
||||||
}
|
}
|
||||||
|
|
@ -151,8 +155,8 @@ static void calcPopupSize(const AppContextT *ctx, const MenuT *menu, int32_t *pw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*pw = maxW + CHROME_TITLE_PAD * 2 + 8 + (hasSub ? SUBMENU_ARROW_WIDTH : 0) + (hasCheck ? MENU_CHECK_WIDTH : 0);
|
*pw = maxW + CHROME_TITLE_PAD * 2 + POPUP_ITEM_PAD_H + (hasSub ? SUBMENU_ARROW_WIDTH : 0) + (hasCheck ? MENU_CHECK_WIDTH : 0);
|
||||||
*ph = menu->itemCount * ctx->font.charHeight + 4;
|
*ph = menu->itemCount * ctx->font.charHeight + POPUP_BEVEL_WIDTH * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -380,16 +384,16 @@ static void compositeAndFlush(AppContextT *ctx) {
|
||||||
smBevel.highlight = ctx->colors.windowHighlight;
|
smBevel.highlight = ctx->colors.windowHighlight;
|
||||||
smBevel.shadow = ctx->colors.windowShadow;
|
smBevel.shadow = ctx->colors.windowShadow;
|
||||||
smBevel.face = ctx->colors.menuBg;
|
smBevel.face = ctx->colors.menuBg;
|
||||||
smBevel.width = 2;
|
smBevel.width = POPUP_BEVEL_WIDTH;
|
||||||
drawBevel(d, ops, ctx->sysMenu.popupX, ctx->sysMenu.popupY, ctx->sysMenu.popupW, ctx->sysMenu.popupH, &smBevel);
|
drawBevel(d, ops, ctx->sysMenu.popupX, ctx->sysMenu.popupY, ctx->sysMenu.popupW, ctx->sysMenu.popupH, &smBevel);
|
||||||
|
|
||||||
int32_t itemY = ctx->sysMenu.popupY + 2;
|
int32_t itemY = ctx->sysMenu.popupY + POPUP_BEVEL_WIDTH;
|
||||||
|
|
||||||
for (int32_t k = 0; k < ctx->sysMenu.itemCount; k++) {
|
for (int32_t k = 0; k < ctx->sysMenu.itemCount; k++) {
|
||||||
SysMenuItemT *item = &ctx->sysMenu.items[k];
|
SysMenuItemT *item = &ctx->sysMenu.items[k];
|
||||||
|
|
||||||
if (item->separator) {
|
if (item->separator) {
|
||||||
drawHLine(d, ops, ctx->sysMenu.popupX + 2, itemY + ctx->font.charHeight / 2, ctx->sysMenu.popupW - 4, ctx->colors.windowShadow);
|
drawHLine(d, ops, ctx->sysMenu.popupX + POPUP_BEVEL_WIDTH, itemY + ctx->font.charHeight / 2, ctx->sysMenu.popupW - POPUP_BEVEL_WIDTH * 2, ctx->colors.windowShadow);
|
||||||
itemY += ctx->font.charHeight;
|
itemY += ctx->font.charHeight;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -402,8 +406,8 @@ static void compositeAndFlush(AppContextT *ctx) {
|
||||||
fg = ctx->colors.menuHighlightFg;
|
fg = ctx->colors.menuHighlightFg;
|
||||||
}
|
}
|
||||||
|
|
||||||
rectFill(d, ops, ctx->sysMenu.popupX + 2, itemY, ctx->sysMenu.popupW - 4, ctx->font.charHeight, bg);
|
rectFill(d, ops, ctx->sysMenu.popupX + POPUP_BEVEL_WIDTH, itemY, ctx->sysMenu.popupW - POPUP_BEVEL_WIDTH * 2, ctx->font.charHeight, bg);
|
||||||
drawTextAccel(d, ops, &ctx->font, ctx->sysMenu.popupX + CHROME_TITLE_PAD + 2, itemY, item->label, fg, bg, true);
|
drawTextAccel(d, ops, &ctx->font, ctx->sysMenu.popupX + CHROME_TITLE_PAD + POPUP_BEVEL_WIDTH, itemY, item->label, fg, bg, true);
|
||||||
|
|
||||||
itemY += ctx->font.charHeight;
|
itemY += ctx->font.charHeight;
|
||||||
}
|
}
|
||||||
|
|
@ -661,7 +665,7 @@ static void dispatchEvents(AppContextT *ctx) {
|
||||||
my >= ctx->sysMenu.popupY && my < ctx->sysMenu.popupY + ctx->sysMenu.popupH) {
|
my >= ctx->sysMenu.popupY && my < ctx->sysMenu.popupY + ctx->sysMenu.popupH) {
|
||||||
|
|
||||||
// Hover tracking
|
// Hover tracking
|
||||||
int32_t relY = my - ctx->sysMenu.popupY - 2;
|
int32_t relY = my - ctx->sysMenu.popupY - POPUP_BEVEL_WIDTH;
|
||||||
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
|
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
|
||||||
|
|
||||||
if (itemIdx >= 0 && itemIdx < ctx->sysMenu.itemCount && ctx->sysMenu.items[itemIdx].separator) {
|
if (itemIdx >= 0 && itemIdx < ctx->sysMenu.itemCount && ctx->sysMenu.items[itemIdx].separator) {
|
||||||
|
|
@ -701,7 +705,7 @@ static void dispatchEvents(AppContextT *ctx) {
|
||||||
|
|
||||||
if (inCurrent) {
|
if (inCurrent) {
|
||||||
// Hover tracking in current level
|
// Hover tracking in current level
|
||||||
int32_t relY = my - ctx->popup.popupY - 2;
|
int32_t relY = my - ctx->popup.popupY - POPUP_BEVEL_WIDTH;
|
||||||
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
|
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
|
||||||
|
|
||||||
if (itemIdx < 0) {
|
if (itemIdx < 0) {
|
||||||
|
|
@ -781,7 +785,7 @@ static void dispatchEvents(AppContextT *ctx) {
|
||||||
closePopupLevel(ctx);
|
closePopupLevel(ctx);
|
||||||
|
|
||||||
// Now current level IS this parent — update hover
|
// Now current level IS this parent — update hover
|
||||||
int32_t relY = my - ctx->popup.popupY - 2;
|
int32_t relY = my - ctx->popup.popupY - POPUP_BEVEL_WIDTH;
|
||||||
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
|
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
|
||||||
|
|
||||||
if (itemIdx < 0) {
|
if (itemIdx < 0) {
|
||||||
|
|
@ -970,17 +974,17 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
|
||||||
popBevel.highlight = ctx->colors.windowHighlight;
|
popBevel.highlight = ctx->colors.windowHighlight;
|
||||||
popBevel.shadow = ctx->colors.windowShadow;
|
popBevel.shadow = ctx->colors.windowShadow;
|
||||||
popBevel.face = ctx->colors.menuBg;
|
popBevel.face = ctx->colors.menuBg;
|
||||||
popBevel.width = 2;
|
popBevel.width = POPUP_BEVEL_WIDTH;
|
||||||
drawBevel(d, ops, px, py, pw, ph, &popBevel);
|
drawBevel(d, ops, px, py, pw, ph, &popBevel);
|
||||||
|
|
||||||
// Draw menu items
|
// Draw menu items
|
||||||
int32_t itemY = py + 2;
|
int32_t itemY = py + POPUP_BEVEL_WIDTH;
|
||||||
|
|
||||||
for (int32_t k = 0; k < menu->itemCount; k++) {
|
for (int32_t k = 0; k < menu->itemCount; k++) {
|
||||||
const MenuItemT *item = &menu->items[k];
|
const MenuItemT *item = &menu->items[k];
|
||||||
|
|
||||||
if (item->separator) {
|
if (item->separator) {
|
||||||
drawHLine(d, ops, px + 2, itemY + ctx->font.charHeight / 2, pw - 4, ctx->colors.windowShadow);
|
drawHLine(d, ops, px + POPUP_BEVEL_WIDTH, itemY + ctx->font.charHeight / 2, pw - POPUP_BEVEL_WIDTH * 2, ctx->colors.windowShadow);
|
||||||
itemY += ctx->font.charHeight;
|
itemY += ctx->font.charHeight;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -993,7 +997,7 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
|
||||||
fg = ctx->colors.menuHighlightFg;
|
fg = ctx->colors.menuHighlightFg;
|
||||||
}
|
}
|
||||||
|
|
||||||
rectFill(d, ops, px + 2, itemY, pw - 4, ctx->font.charHeight, bg);
|
rectFill(d, ops, px + POPUP_BEVEL_WIDTH, itemY, pw - POPUP_BEVEL_WIDTH * 2, ctx->font.charHeight, bg);
|
||||||
|
|
||||||
// Split label at tab: left part is the menu text, right part is the shortcut
|
// Split label at tab: left part is the menu text, right part is the shortcut
|
||||||
const char *tab = strchr(item->label, '\t');
|
const char *tab = strchr(item->label, '\t');
|
||||||
|
|
@ -1009,11 +1013,11 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
|
||||||
memcpy(leftBuf, item->label, leftLen);
|
memcpy(leftBuf, item->label, leftLen);
|
||||||
leftBuf[leftLen] = '\0';
|
leftBuf[leftLen] = '\0';
|
||||||
|
|
||||||
drawTextAccel(d, ops, &ctx->font, px + CHROME_TITLE_PAD + 2 + checkMargin, itemY, leftBuf, fg, bg, true);
|
drawTextAccel(d, ops, &ctx->font, px + CHROME_TITLE_PAD + POPUP_BEVEL_WIDTH + checkMargin, itemY, leftBuf, fg, bg, true);
|
||||||
|
|
||||||
const char *right = tab + 1;
|
const char *right = tab + 1;
|
||||||
int32_t rightW = textWidth(&ctx->font, right);
|
int32_t rightW = textWidth(&ctx->font, right);
|
||||||
int32_t rightX = px + pw - rightW - CHROME_TITLE_PAD - 4;
|
int32_t rightX = px + pw - rightW - CHROME_TITLE_PAD - POPUP_BEVEL_WIDTH * 2;
|
||||||
|
|
||||||
if (item->subMenu) {
|
if (item->subMenu) {
|
||||||
rightX -= SUBMENU_ARROW_WIDTH;
|
rightX -= SUBMENU_ARROW_WIDTH;
|
||||||
|
|
@ -1021,13 +1025,13 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
|
||||||
|
|
||||||
drawText(d, ops, &ctx->font, rightX, itemY, right, fg, bg, true);
|
drawText(d, ops, &ctx->font, rightX, itemY, right, fg, bg, true);
|
||||||
} else {
|
} else {
|
||||||
drawTextAccel(d, ops, &ctx->font, px + CHROME_TITLE_PAD + 2 + checkMargin, itemY, item->label, fg, bg, true);
|
drawTextAccel(d, ops, &ctx->font, px + CHROME_TITLE_PAD + POPUP_BEVEL_WIDTH + checkMargin, itemY, item->label, fg, bg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw check/radio indicator
|
// Draw check/radio indicator
|
||||||
if (item->checked) {
|
if (item->checked) {
|
||||||
int32_t cy = itemY + ctx->font.charHeight / 2;
|
int32_t cy = itemY + ctx->font.charHeight / 2;
|
||||||
int32_t cx = px + 2 + MENU_CHECK_WIDTH / 2;
|
int32_t cx = px + POPUP_BEVEL_WIDTH + MENU_CHECK_WIDTH / 2;
|
||||||
|
|
||||||
if (item->type == MenuItemCheckE) {
|
if (item->type == MenuItemCheckE) {
|
||||||
// Checkmark: small tick shape
|
// Checkmark: small tick shape
|
||||||
|
|
@ -1049,14 +1053,14 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
|
||||||
|
|
||||||
// Draw submenu arrow indicator
|
// Draw submenu arrow indicator
|
||||||
if (item->subMenu) {
|
if (item->subMenu) {
|
||||||
int32_t arrowX = px + pw - SUBMENU_ARROW_WIDTH - 2;
|
int32_t arrowX = px + pw - SUBMENU_ARROW_WIDTH - POPUP_BEVEL_WIDTH;
|
||||||
int32_t arrowY = itemY + ctx->font.charHeight / 2;
|
int32_t arrowY = itemY + ctx->font.charHeight / 2;
|
||||||
|
|
||||||
for (int32_t row = -3; row <= 3; row++) {
|
for (int32_t row = -SUBMENU_ARROW_HALF; row <= SUBMENU_ARROW_HALF; row++) {
|
||||||
int32_t len = 4 - (row < 0 ? -row : row);
|
int32_t len = SUBMENU_ARROW_HALF + 1 - (row < 0 ? -row : row);
|
||||||
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
drawHLine(d, ops, arrowX + (row < 0 ? 3 + row : 3 - row), arrowY + row, len, fg);
|
drawHLine(d, ops, arrowX + (row < 0 ? SUBMENU_ARROW_HALF + row : SUBMENU_ARROW_HALF - row), arrowY + row, len, fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2078,8 +2082,8 @@ static void openSubMenu(AppContextT *ctx) {
|
||||||
|
|
||||||
// Open submenu at right edge of current popup, aligned with hovered item
|
// Open submenu at right edge of current popup, aligned with hovered item
|
||||||
ctx->popup.menu = item->subMenu;
|
ctx->popup.menu = item->subMenu;
|
||||||
ctx->popup.popupX = pl->popupX + pl->popupW - 2;
|
ctx->popup.popupX = pl->popupX + pl->popupW - POPUP_BEVEL_WIDTH;
|
||||||
ctx->popup.popupY = pl->popupY + 2 + idx * ctx->font.charHeight;
|
ctx->popup.popupY = pl->popupY + POPUP_BEVEL_WIDTH + idx * ctx->font.charHeight;
|
||||||
ctx->popup.hoverItem = -1;
|
ctx->popup.hoverItem = -1;
|
||||||
|
|
||||||
calcPopupSize(ctx, ctx->popup.menu, &ctx->popup.popupW, &ctx->popup.popupH);
|
calcPopupSize(ctx, ctx->popup.menu, &ctx->popup.popupW, &ctx->popup.popupH);
|
||||||
|
|
@ -2178,8 +2182,8 @@ static void openSysMenu(AppContextT *ctx, WindowT *win) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->sysMenu.popupW = maxW + CHROME_TITLE_PAD * 2 + 8;
|
ctx->sysMenu.popupW = maxW + CHROME_TITLE_PAD * 2 + POPUP_ITEM_PAD_H;
|
||||||
ctx->sysMenu.popupH = ctx->sysMenu.itemCount * ctx->font.charHeight + 4;
|
ctx->sysMenu.popupH = ctx->sysMenu.itemCount * ctx->font.charHeight + POPUP_BEVEL_WIDTH * 2;
|
||||||
ctx->sysMenu.hoverItem = -1;
|
ctx->sysMenu.hoverItem = -1;
|
||||||
ctx->sysMenu.active = true;
|
ctx->sysMenu.active = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,12 @@
|
||||||
#define BUTTON_WIDTH 80
|
#define BUTTON_WIDTH 80
|
||||||
#define BUTTON_HEIGHT 24
|
#define BUTTON_HEIGHT 24
|
||||||
#define BUTTON_GAP 8
|
#define BUTTON_GAP 8
|
||||||
|
#define ICON_GLYPH_SIZE 24 // icon glyph drawing area (pixels)
|
||||||
|
#define ICON_GLYPH_CENTER 12 // center of icon glyph (ICON_GLYPH_SIZE / 2)
|
||||||
|
#define ICON_OUTER_R2 144 // outer circle radius squared (12*12)
|
||||||
|
#define ICON_INNER_R2 100 // inner circle radius squared (10*10)
|
||||||
|
#define FD_DIALOG_WIDTH 360 // file dialog window width
|
||||||
|
#define FD_DIALOG_HEIGHT 340 // file dialog window height
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Prototypes
|
// Prototypes
|
||||||
|
|
@ -75,13 +81,13 @@ static MsgBoxStateT sMsgBox;
|
||||||
static void drawIconGlyph(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t iconType, uint32_t color) {
|
static void drawIconGlyph(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t iconType, uint32_t color) {
|
||||||
if (iconType == MB_ICONINFO) {
|
if (iconType == MB_ICONINFO) {
|
||||||
// Circle outline with 'i'
|
// Circle outline with 'i'
|
||||||
for (int32_t row = 0; row < 24; row++) {
|
for (int32_t row = 0; row < ICON_GLYPH_SIZE; row++) {
|
||||||
for (int32_t col = 0; col < 24; col++) {
|
for (int32_t col = 0; col < ICON_GLYPH_SIZE; col++) {
|
||||||
int32_t dx = col - 12;
|
int32_t dx = col - ICON_GLYPH_CENTER;
|
||||||
int32_t dy = row - 12;
|
int32_t dy = row - ICON_GLYPH_CENTER;
|
||||||
int32_t d2 = dx * dx + dy * dy;
|
int32_t d2 = dx * dx + dy * dy;
|
||||||
|
|
||||||
if (d2 <= 144 && d2 >= 100) {
|
if (d2 <= ICON_OUTER_R2 && d2 >= ICON_INNER_R2) {
|
||||||
rectFill(d, ops, x + col, y + row, 1, 1, color);
|
rectFill(d, ops, x + col, y + row, 1, 1, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,15 +97,15 @@ static void drawIconGlyph(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y
|
||||||
rectFill(d, ops, x + 11, y + 10, 2, 8, color);
|
rectFill(d, ops, x + 11, y + 10, 2, 8, color);
|
||||||
} else if (iconType == MB_ICONWARNING) {
|
} else if (iconType == MB_ICONWARNING) {
|
||||||
// Triangle outline with '!'
|
// Triangle outline with '!'
|
||||||
for (int32_t row = 0; row < 24; row++) {
|
for (int32_t row = 0; row < ICON_GLYPH_SIZE; row++) {
|
||||||
int32_t halfW = row / 2;
|
int32_t halfW = row / 2;
|
||||||
int32_t lx = 12 - halfW;
|
int32_t lx = ICON_GLYPH_CENTER - halfW;
|
||||||
int32_t rx = 12 + halfW;
|
int32_t rx = ICON_GLYPH_CENTER + halfW;
|
||||||
|
|
||||||
rectFill(d, ops, x + lx, y + row, 1, 1, color);
|
rectFill(d, ops, x + lx, y + row, 1, 1, color);
|
||||||
rectFill(d, ops, x + rx, y + row, 1, 1, color);
|
rectFill(d, ops, x + rx, y + row, 1, 1, color);
|
||||||
|
|
||||||
if (row == 23) {
|
if (row == ICON_GLYPH_SIZE - 1) {
|
||||||
drawHLine(d, ops, x + lx, y + row, rx - lx + 1, color);
|
drawHLine(d, ops, x + lx, y + row, rx - lx + 1, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,31 +114,31 @@ static void drawIconGlyph(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y
|
||||||
rectFill(d, ops, x + 11, y + 19, 2, 2, color);
|
rectFill(d, ops, x + 11, y + 19, 2, 2, color);
|
||||||
} else if (iconType == MB_ICONERROR) {
|
} else if (iconType == MB_ICONERROR) {
|
||||||
// Circle outline with X
|
// Circle outline with X
|
||||||
for (int32_t row = 0; row < 24; row++) {
|
for (int32_t row = 0; row < ICON_GLYPH_SIZE; row++) {
|
||||||
for (int32_t col = 0; col < 24; col++) {
|
for (int32_t col = 0; col < ICON_GLYPH_SIZE; col++) {
|
||||||
int32_t dx = col - 12;
|
int32_t dx = col - ICON_GLYPH_CENTER;
|
||||||
int32_t dy = row - 12;
|
int32_t dy = row - ICON_GLYPH_CENTER;
|
||||||
int32_t d2 = dx * dx + dy * dy;
|
int32_t d2 = dx * dx + dy * dy;
|
||||||
|
|
||||||
if (d2 <= 144 && d2 >= 100) {
|
if (d2 <= ICON_OUTER_R2 && d2 >= ICON_INNER_R2) {
|
||||||
rectFill(d, ops, x + col, y + row, 1, 1, color);
|
rectFill(d, ops, x + col, y + row, 1, 1, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32_t i = 0; i < 12; i++) {
|
for (int32_t i = 0; i < ICON_GLYPH_CENTER; i++) {
|
||||||
rectFill(d, ops, x + 6 + i, y + 6 + i, 2, 2, color);
|
rectFill(d, ops, x + 6 + i, y + 6 + i, 2, 2, color);
|
||||||
rectFill(d, ops, x + 18 - i, y + 6 + i, 2, 2, color);
|
rectFill(d, ops, x + 18 - i, y + 6 + i, 2, 2, color);
|
||||||
}
|
}
|
||||||
} else if (iconType == MB_ICONQUESTION) {
|
} else if (iconType == MB_ICONQUESTION) {
|
||||||
// Circle outline with '?'
|
// Circle outline with '?'
|
||||||
for (int32_t row = 0; row < 24; row++) {
|
for (int32_t row = 0; row < ICON_GLYPH_SIZE; row++) {
|
||||||
for (int32_t col = 0; col < 24; col++) {
|
for (int32_t col = 0; col < ICON_GLYPH_SIZE; col++) {
|
||||||
int32_t dx = col - 12;
|
int32_t dx = col - ICON_GLYPH_CENTER;
|
||||||
int32_t dy = row - 12;
|
int32_t dy = row - ICON_GLYPH_CENTER;
|
||||||
int32_t d2 = dx * dx + dy * dy;
|
int32_t d2 = dx * dx + dy * dy;
|
||||||
|
|
||||||
if (d2 <= 144 && d2 >= 100) {
|
if (d2 <= ICON_OUTER_R2 && d2 >= ICON_INNER_R2) {
|
||||||
rectFill(d, ops, x + col, y + row, 1, 1, color);
|
rectFill(d, ops, x + col, y + row, 1, 1, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +220,7 @@ int32_t dvxMessageBox(AppContextT *ctx, const char *title, const char *message,
|
||||||
|
|
||||||
// Calculate content area sizes
|
// Calculate content area sizes
|
||||||
int32_t msgAreaH = textH + MSG_PADDING * 2;
|
int32_t msgAreaH = textH + MSG_PADDING * 2;
|
||||||
int32_t iconAreaH = hasIcon ? (24 + MSG_PADDING * 2) : 0;
|
int32_t iconAreaH = hasIcon ? (ICON_GLYPH_SIZE + MSG_PADDING * 2) : 0;
|
||||||
|
|
||||||
if (msgAreaH < iconAreaH) {
|
if (msgAreaH < iconAreaH) {
|
||||||
msgAreaH = iconAreaH;
|
msgAreaH = iconAreaH;
|
||||||
|
|
@ -982,8 +988,8 @@ bool dvxFileDialog(AppContextT *ctx, const char *title, int32_t flags, const cha
|
||||||
sFd.curDir[FD_MAX_PATH - 1] = '\0';
|
sFd.curDir[FD_MAX_PATH - 1] = '\0';
|
||||||
|
|
||||||
// Create dialog window
|
// Create dialog window
|
||||||
int32_t dlgW = 360;
|
int32_t dlgW = FD_DIALOG_WIDTH;
|
||||||
int32_t dlgH = 340;
|
int32_t dlgH = FD_DIALOG_HEIGHT;
|
||||||
int32_t winX = (ctx->display.width - dlgW) / 2;
|
int32_t winX = (ctx->display.width - dlgW) / 2;
|
||||||
int32_t winY = (ctx->display.height - dlgH) / 2;
|
int32_t winY = (ctx->display.height - dlgH) / 2;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,8 @@ typedef struct {
|
||||||
// Bitmap font
|
// Bitmap font
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
|
#define FONT_CHAR_WIDTH 8 // fixed glyph width in pixels
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t charWidth; // fixed width per glyph (e.g. 8)
|
int32_t charWidth; // fixed width per glyph (e.g. 8)
|
||||||
int32_t charHeight; // e.g. 14 or 16
|
int32_t charHeight; // e.g. 14 or 16
|
||||||
|
|
|
||||||
|
|
@ -448,6 +448,7 @@ typedef struct WidgetT {
|
||||||
int32_t penSize;
|
int32_t penSize;
|
||||||
int32_t lastX;
|
int32_t lastX;
|
||||||
int32_t lastY;
|
int32_t lastY;
|
||||||
|
void (*onMouse)(struct WidgetT *w, int32_t cx, int32_t cy, bool drag);
|
||||||
} canvas;
|
} canvas;
|
||||||
|
|
||||||
AnsiTermDataT *ansiTerm;
|
AnsiTermDataT *ansiTerm;
|
||||||
|
|
@ -680,6 +681,10 @@ void wgtCanvasSetPenColor(WidgetT *w, uint32_t color);
|
||||||
// Set the pen size in pixels (diameter).
|
// Set the pen size in pixels (diameter).
|
||||||
void wgtCanvasSetPenSize(WidgetT *w, int32_t size);
|
void wgtCanvasSetPenSize(WidgetT *w, int32_t size);
|
||||||
|
|
||||||
|
// Set the mouse callback. Called on click (drag=false) and drag (drag=true)
|
||||||
|
// with canvas-relative coordinates. If NULL (default), mouse events are ignored.
|
||||||
|
void wgtCanvasSetMouseCallback(WidgetT *w, void (*cb)(WidgetT *w, int32_t cx, int32_t cy, bool drag));
|
||||||
|
|
||||||
// Save the canvas to a PNG file. Returns 0 on success, -1 on failure.
|
// Save the canvas to a PNG file. Returns 0 on success, -1 on failure.
|
||||||
int32_t wgtCanvasSave(WidgetT *w, const char *path);
|
int32_t wgtCanvasSave(WidgetT *w, const char *path);
|
||||||
|
|
||||||
|
|
|
||||||
85
dvx/dvxWm.c
85
dvx/dvxWm.c
|
|
@ -17,6 +17,14 @@
|
||||||
|
|
||||||
#define GADGET_PAD 2
|
#define GADGET_PAD 2
|
||||||
#define GADGET_INSET 2 // inset from title bar edges
|
#define GADGET_INSET 2 // inset from title bar edges
|
||||||
|
#define MENU_BAR_GAP 8 // horizontal gap between menu bar labels
|
||||||
|
#define RESIZE_BREAK_INSET 16 // distance from corner to resize groove
|
||||||
|
#define CLOSE_ICON_INSET 3 // close bar inset from gadget edges
|
||||||
|
#define MAXIMIZE_ICON_INSET 2 // maximize box inset from gadget edges
|
||||||
|
#define RESTORE_ICON_INSET 4 // restore box inset from gadget edges
|
||||||
|
#define MINIMIZE_ICON_SIZE 4 // filled square size for minimize icon
|
||||||
|
#define SB_ARROW_ROWS 4 // number of rows in scrollbar arrow glyph
|
||||||
|
#define SB_ARROW_HALF 2 // half-width of scrollbar arrow tip
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Title bar gadget geometry
|
// Title bar gadget geometry
|
||||||
|
|
@ -78,7 +86,7 @@ static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font) {
|
||||||
|
|
||||||
menu->barX = x;
|
menu->barX = x;
|
||||||
menu->barW = labelW;
|
menu->barW = labelW;
|
||||||
x += labelW;
|
x += labelW + MENU_BAR_GAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
win->menuBar->positionsDirty = false;
|
win->menuBar->positionsDirty = false;
|
||||||
|
|
@ -201,6 +209,14 @@ static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fon
|
||||||
rectFill(d, ops, win->x + CHROME_BORDER_WIDTH, barY,
|
rectFill(d, ops, win->x + CHROME_BORDER_WIDTH, barY,
|
||||||
win->w - CHROME_BORDER_WIDTH * 2, barH, colors->menuBg);
|
win->w - CHROME_BORDER_WIDTH * 2, barH, colors->menuBg);
|
||||||
|
|
||||||
|
// Clip menu labels to the menu bar area
|
||||||
|
int32_t savedClipX = d->clipX;
|
||||||
|
int32_t savedClipY = d->clipY;
|
||||||
|
int32_t savedClipW = d->clipW;
|
||||||
|
int32_t savedClipH = d->clipH;
|
||||||
|
setClipRect(d, win->x + CHROME_BORDER_WIDTH, barY,
|
||||||
|
win->w - CHROME_BORDER_WIDTH * 2, barH);
|
||||||
|
|
||||||
// Draw each menu label
|
// Draw each menu label
|
||||||
for (int32_t i = 0; i < win->menuBar->menuCount; i++) {
|
for (int32_t i = 0; i < win->menuBar->menuCount; i++) {
|
||||||
MenuT *menu = &win->menuBar->menus[i];
|
MenuT *menu = &win->menuBar->menus[i];
|
||||||
|
|
@ -211,6 +227,8 @@ static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fon
|
||||||
colors->menuFg, colors->menuBg, true);
|
colors->menuFg, colors->menuBg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setClipRect(d, savedClipX, savedClipY, savedClipW, savedClipH);
|
||||||
|
|
||||||
// Draw bottom separator line
|
// Draw bottom separator line
|
||||||
drawHLine(d, ops, win->x + CHROME_BORDER_WIDTH, barY + barH - 1,
|
drawHLine(d, ops, win->x + CHROME_BORDER_WIDTH, barY + barH - 1,
|
||||||
win->w - CHROME_BORDER_WIDTH * 2, colors->windowShadow);
|
win->w - CHROME_BORDER_WIDTH * 2, colors->windowShadow);
|
||||||
|
|
@ -234,7 +252,7 @@ static void drawResizeBreaks(DisplayT *d, const BlitOpsT *ops, const ColorScheme
|
||||||
uint32_t hi = colors->windowHighlight;
|
uint32_t hi = colors->windowHighlight;
|
||||||
uint32_t sh = colors->windowShadow;
|
uint32_t sh = colors->windowShadow;
|
||||||
int32_t bw = CHROME_BORDER_WIDTH;
|
int32_t bw = CHROME_BORDER_WIDTH;
|
||||||
int32_t inset = 16; // distance from corner to groove
|
int32_t inset = RESIZE_BREAK_INSET;
|
||||||
int32_t wx = win->x;
|
int32_t wx = win->x;
|
||||||
int32_t wy = win->y;
|
int32_t wy = win->y;
|
||||||
int32_t ww = win->w;
|
int32_t ww = win->w;
|
||||||
|
|
@ -416,23 +434,23 @@ static void drawScrollbarArrow(DisplayT *d, const BlitOpsT *ops, const ColorSche
|
||||||
int32_t cy = y + size / 2;
|
int32_t cy = y + size / 2;
|
||||||
uint32_t fg = colors->contentFg;
|
uint32_t fg = colors->contentFg;
|
||||||
|
|
||||||
// Draw a small 5-row triangle
|
// Draw a small triangle
|
||||||
for (int32_t i = 0; i < 4; i++) {
|
for (int32_t i = 0; i < SB_ARROW_ROWS; i++) {
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case 0: // up
|
case 0: // up
|
||||||
drawHLine(d, ops, cx - i, cy - 2 + i, 1 + i * 2, fg);
|
drawHLine(d, ops, cx - i, cy - SB_ARROW_HALF + i, 1 + i * 2, fg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: // down
|
case 1: // down
|
||||||
drawHLine(d, ops, cx - i, cy + 2 - i, 1 + i * 2, fg);
|
drawHLine(d, ops, cx - i, cy + SB_ARROW_HALF - i, 1 + i * 2, fg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // left
|
case 2: // left
|
||||||
drawVLine(d, ops, cx - 2 + i, cy - i, 1 + i * 2, fg);
|
drawVLine(d, ops, cx - SB_ARROW_HALF + i, cy - i, 1 + i * 2, fg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: // right
|
case 3: // right
|
||||||
drawVLine(d, ops, cx + 2 - i, cy - i, 1 + i * 2, fg);
|
drawVLine(d, ops, cx + SB_ARROW_HALF - i, cy - i, 1 + i * 2, fg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -456,32 +474,34 @@ static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
|
||||||
// Close gadget on the LEFT (system menu / close)
|
// Close gadget on the LEFT (system menu / close)
|
||||||
drawTitleGadget(d, ops, colors, g.closeX, g.gadgetY, g.gadgetS);
|
drawTitleGadget(d, ops, colors, g.closeX, g.gadgetY, g.gadgetS);
|
||||||
// Horizontal bar icon inside close gadget
|
// Horizontal bar icon inside close gadget
|
||||||
drawHLine(d, ops, g.closeX + 3, g.gadgetY + g.gadgetS / 2,
|
drawHLine(d, ops, g.closeX + CLOSE_ICON_INSET, g.gadgetY + g.gadgetS / 2,
|
||||||
g.gadgetS - 6, colors->contentFg);
|
g.gadgetS - CLOSE_ICON_INSET * 2, colors->contentFg);
|
||||||
|
|
||||||
if (g.maxX >= 0) {
|
if (g.maxX >= 0) {
|
||||||
// Maximize/restore gadget on the far RIGHT
|
// Maximize/restore gadget on the far RIGHT
|
||||||
drawTitleGadget(d, ops, colors, g.maxX, g.gadgetY, g.gadgetS);
|
drawTitleGadget(d, ops, colors, g.maxX, g.gadgetY, g.gadgetS);
|
||||||
|
|
||||||
if (win->maximized) {
|
if (win->maximized) {
|
||||||
// Restore icon: larger box outline filling more of the gadget
|
// Restore icon: small box outline
|
||||||
int32_t bx = g.maxX + 2;
|
int32_t bx = g.maxX + RESTORE_ICON_INSET;
|
||||||
int32_t by = g.gadgetY + 2;
|
int32_t by = g.gadgetY + RESTORE_ICON_INSET;
|
||||||
int32_t bs = g.gadgetS - 4;
|
int32_t bs = g.gadgetS - RESTORE_ICON_INSET * 2;
|
||||||
|
rectFill(d, ops, bx, by, bs, bs, colors->buttonFace);
|
||||||
|
drawHLine(d, ops, bx, by, bs, colors->contentFg);
|
||||||
|
drawHLine(d, ops, bx, by + bs - 1, bs, colors->contentFg);
|
||||||
|
drawVLine(d, ops, bx, by, bs, colors->contentFg);
|
||||||
|
drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg);
|
||||||
|
} else {
|
||||||
|
// Maximize icon: larger box outline filling more of the gadget
|
||||||
|
int32_t bx = g.maxX + MAXIMIZE_ICON_INSET;
|
||||||
|
int32_t by = g.gadgetY + MAXIMIZE_ICON_INSET;
|
||||||
|
int32_t bs = g.gadgetS - MAXIMIZE_ICON_INSET * 2;
|
||||||
rectFill(d, ops, bx, by, bs, bs, colors->buttonFace);
|
rectFill(d, ops, bx, by, bs, bs, colors->buttonFace);
|
||||||
drawHLine(d, ops, bx, by, bs, colors->contentFg);
|
drawHLine(d, ops, bx, by, bs, colors->contentFg);
|
||||||
drawHLine(d, ops, bx, by + 1, bs, colors->contentFg);
|
drawHLine(d, ops, bx, by + 1, bs, colors->contentFg);
|
||||||
drawHLine(d, ops, bx, by + bs - 1, bs, colors->contentFg);
|
drawHLine(d, ops, bx, by + bs - 1, bs, colors->contentFg);
|
||||||
drawVLine(d, ops, bx, by, bs, colors->contentFg);
|
drawVLine(d, ops, bx, by, bs, colors->contentFg);
|
||||||
drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg);
|
drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg);
|
||||||
} else {
|
|
||||||
// Maximize icon: small box outline
|
|
||||||
rectFill(d, ops, g.maxX + 3, g.gadgetY + 3,
|
|
||||||
g.gadgetS - 6, g.gadgetS - 6, colors->buttonFace);
|
|
||||||
drawHLine(d, ops, g.maxX + 3, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
|
|
||||||
drawHLine(d, ops, g.maxX + 3, g.gadgetY + g.gadgetS - 4, g.gadgetS - 6, colors->contentFg);
|
|
||||||
drawVLine(d, ops, g.maxX + 3, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
|
|
||||||
drawVLine(d, ops, g.maxX + g.gadgetS - 4, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -489,9 +509,8 @@ static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
|
||||||
if (g.minX >= 0) {
|
if (g.minX >= 0) {
|
||||||
drawTitleGadget(d, ops, colors, g.minX, g.gadgetY, g.gadgetS);
|
drawTitleGadget(d, ops, colors, g.minX, g.gadgetY, g.gadgetS);
|
||||||
// Small square centered in minimize gadget
|
// Small square centered in minimize gadget
|
||||||
int32_t miniSize = 4;
|
rectFill(d, ops, g.minX + (g.gadgetS - MINIMIZE_ICON_SIZE) / 2, g.gadgetY + (g.gadgetS - MINIMIZE_ICON_SIZE) / 2,
|
||||||
rectFill(d, ops, g.minX + (g.gadgetS - miniSize) / 2, g.gadgetY + (g.gadgetS - miniSize) / 2,
|
MINIMIZE_ICON_SIZE, MINIMIZE_ICON_SIZE, colors->contentFg);
|
||||||
miniSize, miniSize, colors->contentFg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title text — centered between close gadget and minimize, truncated to fit
|
// Title text — centered between close gadget and minimize, truncated to fit
|
||||||
|
|
@ -1343,9 +1362,9 @@ int32_t wmMinimizedIconHit(const WindowStackT *stack, const DisplayT *d, int32_t
|
||||||
|
|
||||||
static void wmMinWindowSize(const WindowT *win, int32_t *minW, int32_t *minH) {
|
static void wmMinWindowSize(const WindowT *win, int32_t *minW, int32_t *minH) {
|
||||||
// Title bar: close gadget + padding + 1 char of title + padding
|
// Title bar: close gadget + padding + 1 char of title + padding
|
||||||
int32_t gadgetS = CHROME_TITLE_HEIGHT - 4;
|
int32_t gadgetS = CHROME_TITLE_HEIGHT - GADGET_INSET * 2;
|
||||||
int32_t gadgetPad = 2;
|
int32_t gadgetPad = GADGET_PAD;
|
||||||
int32_t charW = 8; // bitmap font char width
|
int32_t charW = FONT_CHAR_WIDTH;
|
||||||
|
|
||||||
// Minimum title bar width: close gadget + 1 char + minimize gadget + padding
|
// Minimum title bar width: close gadget + 1 char + minimize gadget + padding
|
||||||
int32_t titleMinW = gadgetPad + gadgetS + gadgetPad + charW + gadgetPad + gadgetS + gadgetPad;
|
int32_t titleMinW = gadgetPad + gadgetS + gadgetPad + charW + gadgetPad + gadgetS + gadgetPad;
|
||||||
|
|
@ -1357,6 +1376,16 @@ static void wmMinWindowSize(const WindowT *win, int32_t *minW, int32_t *minH) {
|
||||||
|
|
||||||
*minW = titleMinW + CHROME_BORDER_WIDTH * 2;
|
*minW = titleMinW + CHROME_BORDER_WIDTH * 2;
|
||||||
|
|
||||||
|
// Menu bar width: ensure window is wide enough to show all menu labels
|
||||||
|
if (win->menuBar && win->menuBar->menuCount > 0) {
|
||||||
|
MenuT *last = &win->menuBar->menus[win->menuBar->menuCount - 1];
|
||||||
|
int32_t menuW = last->barX + last->barW + CHROME_TOTAL_SIDE;
|
||||||
|
|
||||||
|
if (menuW > *minW) {
|
||||||
|
*minW = menuW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Minimum height: border + title + inner border + some content + bottom chrome
|
// Minimum height: border + title + inner border + some content + bottom chrome
|
||||||
*minH = CHROME_TOTAL_TOP + CHROME_TOTAL_BOTTOM + 1;
|
*minH = CHROME_TOTAL_TOP + CHROME_TOTAL_BOTTOM + 1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,8 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
|
||||||
int32_t textY = w->y + (w->h - font->charHeight) / 2;
|
int32_t textY = w->y + (w->h - font->charHeight) / 2;
|
||||||
|
|
||||||
if (w->as.button.pressed) {
|
if (w->as.button.pressed) {
|
||||||
textX++;
|
textX += BUTTON_PRESS_OFFSET;
|
||||||
textY++;
|
textY += BUTTON_PRESS_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!w->enabled) {
|
if (!w->enabled) {
|
||||||
|
|
@ -109,7 +109,7 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w->focused) {
|
if (w->focused) {
|
||||||
int32_t off = w->as.button.pressed ? 1 : 0;
|
int32_t off = w->as.button.pressed ? BUTTON_PRESS_OFFSET : 0;
|
||||||
drawFocusRect(d, ops, w->x + 3 + off, w->y + 3 + off, w->w - 6, w->h - 6, fg);
|
drawFocusRect(d, ops, w->x + BUTTON_FOCUS_INSET + off, w->y + BUTTON_FOCUS_INSET + off, w->w - BUTTON_FOCUS_INSET * 2, w->h - BUTTON_FOCUS_INSET * 2, fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -621,6 +621,17 @@ int32_t wgtCanvasSave(WidgetT *w, const char *path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// wgtCanvasSetMouseCallback
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
void wgtCanvasSetMouseCallback(WidgetT *w, void (*cb)(WidgetT *w, int32_t cx, int32_t cy, bool drag)) {
|
||||||
|
if (w && w->type == WidgetCanvasE) {
|
||||||
|
w->as.canvas.onMouse = cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// wgtCanvasSetPenColor
|
// wgtCanvasSetPenColor
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -689,26 +700,25 @@ void widgetCanvasCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||||
|
|
||||||
void widgetCanvasOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
|
void widgetCanvasOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
|
||||||
(void)root;
|
(void)root;
|
||||||
|
|
||||||
|
if (!hit->as.canvas.onMouse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert widget coords to canvas coords
|
// Convert widget coords to canvas coords
|
||||||
int32_t cx = vx - hit->x - CANVAS_BORDER;
|
int32_t cx = vx - hit->x - CANVAS_BORDER;
|
||||||
int32_t cy = vy - hit->y - CANVAS_BORDER;
|
int32_t cy = vy - hit->y - CANVAS_BORDER;
|
||||||
|
|
||||||
if (sDrawingCanvas == hit) {
|
bool drag = (sDrawingCanvas == hit);
|
||||||
// Continuation of a drag stroke — draw line from last to current
|
|
||||||
if (hit->as.canvas.lastX >= 0) {
|
if (!drag) {
|
||||||
canvasDrawLine(hit, hit->as.canvas.lastX, hit->as.canvas.lastY, cx, cy);
|
|
||||||
} else {
|
|
||||||
canvasDrawDot(hit, cx, cy);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// First click — start drawing, place a dot
|
|
||||||
sDrawingCanvas = hit;
|
sDrawingCanvas = hit;
|
||||||
canvasDrawDot(hit, cx, cy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hit->as.canvas.lastX = cx;
|
hit->as.canvas.lastX = cx;
|
||||||
hit->as.canvas.lastY = cy;
|
hit->as.canvas.lastY = cy;
|
||||||
|
|
||||||
|
hit->as.canvas.onMouse(hit, cx, cy, drag);
|
||||||
wgtInvalidatePaint(hit);
|
wgtInvalidatePaint(hit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,8 +262,8 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
||||||
int32_t delta = x - sResizeStartX;
|
int32_t delta = x - sResizeStartX;
|
||||||
int32_t newW = sResizeOrigW + delta;
|
int32_t newW = sResizeOrigW + delta;
|
||||||
|
|
||||||
if (newW < 20) {
|
if (newW < LISTVIEW_MIN_COL_W) {
|
||||||
newW = 20;
|
newW = LISTVIEW_MIN_COL_W;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newW != sResizeListView->as.listView->resolvedColW[sResizeCol]) {
|
if (newW != sResizeListView->as.listView->resolvedColW[sResizeCol]) {
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,8 @@ extern const WidgetClassT *widgetClassTable[];
|
||||||
#define SEPARATOR_THICKNESS 2
|
#define SEPARATOR_THICKNESS 2
|
||||||
#define BUTTON_PAD_H 8
|
#define BUTTON_PAD_H 8
|
||||||
#define BUTTON_PAD_V 4
|
#define BUTTON_PAD_V 4
|
||||||
|
#define BUTTON_FOCUS_INSET 3 // focus rect inset from button edge
|
||||||
|
#define BUTTON_PRESS_OFFSET 1 // text/focus shift when button is pressed
|
||||||
#define CHECKBOX_BOX_SIZE 12
|
#define CHECKBOX_BOX_SIZE 12
|
||||||
#define CHECKBOX_GAP 4
|
#define CHECKBOX_GAP 4
|
||||||
#define FRAME_BEVEL_BORDER 2
|
#define FRAME_BEVEL_BORDER 2
|
||||||
|
|
@ -75,6 +77,7 @@ extern const WidgetClassT *widgetClassTable[];
|
||||||
#define TAB_BORDER 2
|
#define TAB_BORDER 2
|
||||||
#define LISTBOX_BORDER 2
|
#define LISTBOX_BORDER 2
|
||||||
#define LISTVIEW_BORDER 2
|
#define LISTVIEW_BORDER 2
|
||||||
|
#define LISTVIEW_MIN_COL_W 20 // minimum column width during resize
|
||||||
#define TREE_INDENT 16
|
#define TREE_INDENT 16
|
||||||
#define TREE_EXPAND_SIZE 9
|
#define TREE_EXPAND_SIZE 9
|
||||||
#define TREE_ICON_GAP 4
|
#define TREE_ICON_GAP 4
|
||||||
|
|
@ -84,6 +87,8 @@ extern const WidgetClassT *widgetClassTable[];
|
||||||
#define SB_MIN_THUMB 14
|
#define SB_MIN_THUMB 14
|
||||||
#define SPLITTER_BAR_W 5
|
#define SPLITTER_BAR_W 5
|
||||||
#define SPLITTER_MIN_PANE 20
|
#define SPLITTER_MIN_PANE 20
|
||||||
|
#define TOOLBAR_PAD 2 // padding inside toolbar/statusbar
|
||||||
|
#define TOOLBAR_GAP 2 // gap between toolbar/statusbar children
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Inline helpers
|
// Inline helpers
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ void widgetCalcMinSizeBox(WidgetT *w, const BitmapFontT *font) {
|
||||||
|
|
||||||
// Toolbar and StatusBar use tighter padding
|
// Toolbar and StatusBar use tighter padding
|
||||||
if (w->type == WidgetToolbarE || w->type == WidgetStatusBarE) {
|
if (w->type == WidgetToolbarE || w->type == WidgetStatusBarE) {
|
||||||
pad = 2;
|
pad = TOOLBAR_PAD;
|
||||||
gap = 2;
|
gap = TOOLBAR_GAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||||
|
|
@ -145,8 +145,8 @@ void widgetLayoutBox(WidgetT *w, const BitmapFontT *font) {
|
||||||
|
|
||||||
// Toolbar and StatusBar use tighter padding
|
// Toolbar and StatusBar use tighter padding
|
||||||
if (w->type == WidgetToolbarE || w->type == WidgetStatusBarE) {
|
if (w->type == WidgetToolbarE || w->type == WidgetStatusBarE) {
|
||||||
pad = 2;
|
pad = TOOLBAR_PAD;
|
||||||
gap = 2;
|
gap = TOOLBAR_GAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t innerX = w->x + pad + fb;
|
int32_t innerX = w->x + pad + fb;
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
# DVX GUI Demo Makefile for DJGPP cross-compilation
|
|
||||||
|
|
||||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
|
||||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
|
||||||
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
|
|
||||||
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
|
|
||||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../dvx
|
|
||||||
LDFLAGS = -L../lib -ldvx -lm
|
|
||||||
|
|
||||||
OBJDIR = ../obj/dvxdemo
|
|
||||||
BINDIR = ../bin
|
|
||||||
LIBDIR = ../lib
|
|
||||||
|
|
||||||
SRCS = demo.c
|
|
||||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
|
||||||
TARGET = $(BINDIR)/demo.exe
|
|
||||||
BMPS = $(wildcard *.bmp)
|
|
||||||
|
|
||||||
.PHONY: all clean lib
|
|
||||||
|
|
||||||
all: lib $(TARGET) $(addprefix $(BINDIR)/,$(BMPS))
|
|
||||||
|
|
||||||
lib:
|
|
||||||
$(MAKE) -C ../dvx
|
|
||||||
|
|
||||||
$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a | $(BINDIR)
|
|
||||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
|
|
||||||
$(EXE2COFF) $@
|
|
||||||
cat $(CWSDSTUB) $(BINDIR)/demo > $@
|
|
||||||
rm -f $(BINDIR)/demo
|
|
||||||
|
|
||||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
$(OBJDIR):
|
|
||||||
mkdir -p $(OBJDIR)
|
|
||||||
|
|
||||||
$(BINDIR):
|
|
||||||
mkdir -p $(BINDIR)
|
|
||||||
|
|
||||||
$(BINDIR)/%.bmp: %.bmp | $(BINDIR)
|
|
||||||
cp $< $@
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
$(OBJDIR)/demo.o: demo.c ../dvx/dvxApp.h ../dvx/dvxWidget.h
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(OBJDIR) $(BINDIR)
|
|
||||||
|
|
@ -14,7 +14,7 @@ LIBDIR = ../lib
|
||||||
|
|
||||||
SRCS = shellMain.c shellApp.c shellExport.c
|
SRCS = shellMain.c shellApp.c shellExport.c
|
||||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||||
TARGET = $(BINDIR)/dvxshell.exe
|
TARGET = $(BINDIR)/dvx.exe
|
||||||
|
|
||||||
.PHONY: all clean libs
|
.PHONY: all clean libs
|
||||||
|
|
||||||
|
|
@ -25,10 +25,10 @@ libs:
|
||||||
$(MAKE) -C ../tasks
|
$(MAKE) -C ../tasks
|
||||||
|
|
||||||
$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libtasks.a | $(BINDIR)
|
$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libtasks.a | $(BINDIR)
|
||||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) -Wl,-Map=$(BINDIR)/dvxshell.map
|
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) -Wl,-Map=$(BINDIR)/dvx.map
|
||||||
$(EXE2COFF) $@
|
$(EXE2COFF) $@
|
||||||
cat $(CWSDSTUB) $(BINDIR)/dvxshell > $@
|
cat $(CWSDSTUB) $(BINDIR)/dvx > $@
|
||||||
rm -f $(BINDIR)/dvxshell
|
rm -f $(BINDIR)/dvx
|
||||||
|
|
||||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
@ -44,4 +44,4 @@ $(OBJDIR)/shellMain.o: shellMain.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialo
|
||||||
$(OBJDIR)/shellApp.o: shellApp.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
|
$(OBJDIR)/shellApp.o: shellApp.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
|
||||||
$(OBJDIR)/shellExport.o: shellExport.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvx/dvxWm.h ../tasks/taskswitch.h
|
$(OBJDIR)/shellExport.o: shellExport.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvx/dvxWm.h ../tasks/taskswitch.h
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJDIR) $(TARGET)
|
rm -f $(OBJS) $(TARGET) $(BINDIR)/dvx.map $(BINDIR)/dvx.log
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,23 @@ int32_t shellLoadApp(AppContextT *ctx, const char *path) {
|
||||||
app->dxeCtx.shellCtx = ctx;
|
app->dxeCtx.shellCtx = ctx;
|
||||||
app->dxeCtx.appId = id;
|
app->dxeCtx.appId = id;
|
||||||
|
|
||||||
|
// Derive app directory from path (everything up to last '/' or '\')
|
||||||
|
snprintf(app->dxeCtx.appDir, sizeof(app->dxeCtx.appDir), "%s", path);
|
||||||
|
|
||||||
|
char *lastSlash = strrchr(app->dxeCtx.appDir, '/');
|
||||||
|
char *lastBack = strrchr(app->dxeCtx.appDir, '\\');
|
||||||
|
|
||||||
|
if (lastBack > lastSlash) {
|
||||||
|
lastSlash = lastBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSlash) {
|
||||||
|
*lastSlash = '\0';
|
||||||
|
} else {
|
||||||
|
app->dxeCtx.appDir[0] = '.';
|
||||||
|
app->dxeCtx.appDir[1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
// Launch
|
// Launch
|
||||||
sCurrentAppId = id;
|
sCurrentAppId = id;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
AppContextT *shellCtx; // the shell's GUI context
|
AppContextT *shellCtx; // the shell's GUI context
|
||||||
int32_t appId; // this app's ID
|
int32_t appId; // this app's ID
|
||||||
|
char appDir[260]; // directory containing the .app file
|
||||||
} DxeAppContextT;
|
} DxeAppContextT;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -95,7 +96,7 @@ int32_t shellRunningAppCount(void);
|
||||||
// Logging
|
// Logging
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// Write a printf-style message to SHELL.LOG
|
// Write a printf-style message to DVX.LOG
|
||||||
void shellLog(const char *fmt, ...);
|
void shellLog(const char *fmt, ...);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Constants
|
// Constants
|
||||||
|
|
@ -55,12 +56,13 @@ static void onAppButtonClick(WidgetT *w);
|
||||||
static void onPmClose(WindowT *win);
|
static void onPmClose(WindowT *win);
|
||||||
static void onPmMenu(WindowT *win, int32_t menuId);
|
static void onPmMenu(WindowT *win, int32_t menuId);
|
||||||
static void scanAppsDir(void);
|
static void scanAppsDir(void);
|
||||||
|
static void scanAppsDirRecurse(const char *dirPath);
|
||||||
static void showAboutDialog(void);
|
static void showAboutDialog(void);
|
||||||
static void updateStatusText(void);
|
static void updateStatusText(void);
|
||||||
|
|
||||||
// Task Manager
|
// Task Manager
|
||||||
static WindowT *sTmWindow = NULL;
|
static WindowT *sTmWindow = NULL;
|
||||||
static WidgetT *sTmListBox = NULL;
|
static WidgetT *sTmListView = NULL;
|
||||||
static void buildTaskManager(void);
|
static void buildTaskManager(void);
|
||||||
static void onTmClose(WindowT *win);
|
static void onTmClose(WindowT *win);
|
||||||
static void onTmEndTask(WidgetT *w);
|
static void onTmEndTask(WidgetT *w);
|
||||||
|
|
@ -181,10 +183,17 @@ static void buildTaskManager(void) {
|
||||||
|
|
||||||
WidgetT *root = wgtInitWindow(sCtx, sTmWindow);
|
WidgetT *root = wgtInitWindow(sCtx, sTmWindow);
|
||||||
|
|
||||||
// List box of running apps
|
// List view of running apps
|
||||||
sTmListBox = wgtListBox(root);
|
static const ListViewColT tmCols[] = {
|
||||||
sTmListBox->weight = 100;
|
{ "Name", wgtPercent(50), ListViewAlignLeftE },
|
||||||
sTmListBox->prefH = wgtPixels(160);
|
{ "Type", wgtPercent(25), ListViewAlignLeftE },
|
||||||
|
{ "Status", wgtPercent(25), ListViewAlignLeftE },
|
||||||
|
};
|
||||||
|
|
||||||
|
sTmListView = wgtListView(root);
|
||||||
|
sTmListView->weight = 100;
|
||||||
|
sTmListView->prefH = wgtPixels(160);
|
||||||
|
wgtListViewSetColumns(sTmListView, tmCols, 3);
|
||||||
|
|
||||||
// Button row
|
// Button row
|
||||||
WidgetT *btnRow = wgtHBox(root);
|
WidgetT *btnRow = wgtHBox(root);
|
||||||
|
|
@ -278,7 +287,7 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
|
||||||
|
|
||||||
|
|
||||||
static void onTmClose(WindowT *win) {
|
static void onTmClose(WindowT *win) {
|
||||||
sTmListBox = NULL;
|
sTmListView = NULL;
|
||||||
sTmWindow = NULL;
|
sTmWindow = NULL;
|
||||||
dvxDestroyWindow(sCtx, win);
|
dvxDestroyWindow(sCtx, win);
|
||||||
}
|
}
|
||||||
|
|
@ -287,11 +296,11 @@ static void onTmClose(WindowT *win) {
|
||||||
static void onTmEndTask(WidgetT *w) {
|
static void onTmEndTask(WidgetT *w) {
|
||||||
(void)w;
|
(void)w;
|
||||||
|
|
||||||
if (!sTmListBox) {
|
if (!sTmListView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t sel = wgtListBoxGetSelected(sTmListBox);
|
int32_t sel = wgtListViewGetSelected(sTmListView);
|
||||||
|
|
||||||
if (sel < 0) {
|
if (sel < 0) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -320,11 +329,11 @@ static void onTmEndTask(WidgetT *w) {
|
||||||
static void onTmSwitchTo(WidgetT *w) {
|
static void onTmSwitchTo(WidgetT *w) {
|
||||||
(void)w;
|
(void)w;
|
||||||
|
|
||||||
if (!sTmListBox) {
|
if (!sTmListView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t sel = wgtListBoxGetSelected(sTmListBox);
|
int32_t sel = wgtListViewGetSelected(sTmListView);
|
||||||
|
|
||||||
if (sel < 0) {
|
if (sel < 0) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -363,41 +372,69 @@ static void onTmSwitchTo(WidgetT *w) {
|
||||||
|
|
||||||
|
|
||||||
static void refreshTaskList(void) {
|
static void refreshTaskList(void) {
|
||||||
if (!sTmListBox) {
|
if (!sTmListView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *items[SHELL_MAX_APPS];
|
// 3 columns per row: Name, Type, Status
|
||||||
static char names[SHELL_MAX_APPS][SHELL_APP_NAME_MAX + 16];
|
static const char *cells[SHELL_MAX_APPS * 3];
|
||||||
int32_t count = 0;
|
static char typeStrs[SHELL_MAX_APPS][12];
|
||||||
|
int32_t rowCount = 0;
|
||||||
|
|
||||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
||||||
ShellAppT *app = shellGetApp(i);
|
ShellAppT *app = shellGetApp(i);
|
||||||
|
|
||||||
if (app && app->state == AppStateRunningE) {
|
if (app && app->state == AppStateRunningE) {
|
||||||
const char *state = app->hasMainLoop ? "task" : "callback";
|
int32_t base = rowCount * 3;
|
||||||
snprintf(names[count], sizeof(names[count]), "%s [%s]", app->name, state);
|
cells[base] = app->name;
|
||||||
items[count] = names[count];
|
snprintf(typeStrs[rowCount], sizeof(typeStrs[rowCount]), "%s", app->hasMainLoop ? "Task" : "Callback");
|
||||||
count++;
|
cells[base + 1] = typeStrs[rowCount];
|
||||||
|
cells[base + 2] = "Running";
|
||||||
|
rowCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wgtListBoxSetItems(sTmListBox, items, count);
|
wgtListViewSetData(sTmListView, cells, rowCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void scanAppsDir(void) {
|
static void scanAppsDir(void) {
|
||||||
DIR *dir = opendir("apps");
|
sDxeCount = 0;
|
||||||
|
scanAppsDirRecurse("apps");
|
||||||
|
shellLog("Shell: found %ld app(s)", (long)sDxeCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void scanAppsDirRecurse(const char *dirPath) {
|
||||||
|
DIR *dir = opendir(dirPath);
|
||||||
|
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
shellLog("Shell: apps/ directory not found");
|
if (sDxeCount == 0) {
|
||||||
|
shellLog("Shell: %s directory not found", dirPath);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sDxeCount = 0;
|
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
|
|
||||||
while ((ent = readdir(dir)) != NULL && sDxeCount < MAX_DXE_FILES) {
|
while ((ent = readdir(dir)) != NULL && sDxeCount < MAX_DXE_FILES) {
|
||||||
|
// Skip . and ..
|
||||||
|
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char fullPath[MAX_PATH_LEN];
|
||||||
|
snprintf(fullPath, sizeof(fullPath), "%s/%s", dirPath, ent->d_name);
|
||||||
|
|
||||||
|
// Check if this is a directory — recurse into it
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(fullPath, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||||
|
scanAppsDirRecurse(fullPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int32_t len = strlen(ent->d_name);
|
int32_t len = strlen(ent->d_name);
|
||||||
|
|
||||||
if (len < 5) {
|
if (len < 5) {
|
||||||
|
|
@ -428,12 +465,11 @@ static void scanAppsDir(void) {
|
||||||
entry->name[0] -= 32;
|
entry->name[0] -= 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(entry->path, sizeof(entry->path), "apps/%s", ent->d_name);
|
snprintf(entry->path, sizeof(entry->path), "%s", fullPath);
|
||||||
sDxeCount++;
|
sDxeCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
shellLog("Shell: found %ld app(s)", (long)sDxeCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,8 @@ DXE_EXPORT_TABLE(shellExportTable)
|
||||||
DXE_EXPORT(wmSetIcon)
|
DXE_EXPORT(wmSetIcon)
|
||||||
DXE_EXPORT(wmCreateMenu)
|
DXE_EXPORT(wmCreateMenu)
|
||||||
DXE_EXPORT(wmFreeMenu)
|
DXE_EXPORT(wmFreeMenu)
|
||||||
|
DXE_EXPORT(wmUpdateContentRect)
|
||||||
|
DXE_EXPORT(wmReallocContentBuf)
|
||||||
|
|
||||||
// dvxWidget.h — window integration
|
// dvxWidget.h — window integration
|
||||||
DXE_EXPORT(wgtInitWindow)
|
DXE_EXPORT(wgtInitWindow)
|
||||||
|
|
@ -278,6 +280,7 @@ DXE_EXPORT_TABLE(shellExportTable)
|
||||||
// dvxWidget.h — canvas
|
// dvxWidget.h — canvas
|
||||||
DXE_EXPORT(wgtCanvas)
|
DXE_EXPORT(wgtCanvas)
|
||||||
DXE_EXPORT(wgtCanvasClear)
|
DXE_EXPORT(wgtCanvasClear)
|
||||||
|
DXE_EXPORT(wgtCanvasSetMouseCallback)
|
||||||
DXE_EXPORT(wgtCanvasSetPenColor)
|
DXE_EXPORT(wgtCanvasSetPenColor)
|
||||||
DXE_EXPORT(wgtCanvasSetPenSize)
|
DXE_EXPORT(wgtCanvasSetPenSize)
|
||||||
DXE_EXPORT(wgtCanvasSave)
|
DXE_EXPORT(wgtCanvasSave)
|
||||||
|
|
@ -301,6 +304,7 @@ DXE_EXPORT_TABLE(shellExportTable)
|
||||||
// dvxWidget.h — operations
|
// dvxWidget.h — operations
|
||||||
DXE_EXPORT(wgtInvalidate)
|
DXE_EXPORT(wgtInvalidate)
|
||||||
DXE_EXPORT(wgtInvalidatePaint)
|
DXE_EXPORT(wgtInvalidatePaint)
|
||||||
|
DXE_EXPORT(wgtSetDebugLayout)
|
||||||
DXE_EXPORT(wgtSetText)
|
DXE_EXPORT(wgtSetText)
|
||||||
DXE_EXPORT(wgtGetText)
|
DXE_EXPORT(wgtGetText)
|
||||||
DXE_EXPORT(wgtSetEnabled)
|
DXE_EXPORT(wgtSetEnabled)
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ static void logCrash(int sig) {
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// shellLog — append a line to SHELL.LOG
|
// shellLog — append a line to DVX.LOG
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
void shellLog(const char *fmt, ...) {
|
void shellLog(const char *fmt, ...) {
|
||||||
|
|
@ -163,7 +163,7 @@ void shellRegisterDesktopUpdate(void (*updateFn)(void)) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
sLogFile = fopen("shell.log", "w");
|
sLogFile = fopen("dvx.log", "w");
|
||||||
shellLog("DVX Shell starting...");
|
shellLog("DVX Shell starting...");
|
||||||
|
|
||||||
// Initialize GUI
|
// Initialize GUI
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,9 @@ DEMO_TARGET = $(BINDIR)/tsdemo.exe
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
all: $(TARGET) $(DEMO_TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
|
demo: $(DEMO_TARGET)
|
||||||
|
|
||||||
$(TARGET): $(OBJS) | $(LIBDIR)
|
$(TARGET): $(OBJS) | $(LIBDIR)
|
||||||
$(AR) rcs $@ $(OBJS)
|
$(AR) rcs $@ $(OBJS)
|
||||||
|
|
@ -52,4 +54,4 @@ $(OBJDIR)/taskswitch.o: taskswitch.c taskswitch.h thirdparty/stb_ds.h
|
||||||
$(OBJDIR)/demo.o: demo.c taskswitch.h
|
$(OBJDIR)/demo.o: demo.c taskswitch.h
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJDIR) $(TARGET) $(DEMO_TARGET)
|
rm -f $(OBJS) $(DEMO_OBJS) $(TARGET) $(DEMO_TARGET)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue