Code cleanup.
This commit is contained in:
parent
b1040a655e
commit
7ae7ea1a97
18 changed files with 463 additions and 279 deletions
|
|
@ -35,6 +35,11 @@
|
|||
// Module state
|
||||
// ============================================================
|
||||
|
||||
#define CLOCK_WIN_W 200
|
||||
#define CLOCK_WIN_H 100
|
||||
#define CLOCK_MARGIN 40
|
||||
#define CLOCK_DATE_GAP 8
|
||||
|
||||
typedef struct {
|
||||
bool quit;
|
||||
char timeStr[32];
|
||||
|
|
@ -125,7 +130,7 @@ static void onPaint(WindowT *win, RectT *dirty) {
|
|||
// Date string (centered below)
|
||||
int32_t dateW = textWidth(font, sState.dateStr);
|
||||
int32_t dateX = (win->contentW - dateW) / 2;
|
||||
int32_t dateY = timeY + font->charHeight + 8;
|
||||
int32_t dateY = timeY + font->charHeight + CLOCK_DATE_GAP;
|
||||
drawText(&cd, ops, font, dateX, dateY, sState.dateStr, colors->contentFg, colors->contentBg, true);
|
||||
}
|
||||
|
||||
|
|
@ -182,10 +187,10 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
updateTime();
|
||||
|
||||
// Position in the upper-right corner, out of the way of other windows
|
||||
int32_t winW = 200;
|
||||
int32_t winH = 100;
|
||||
int32_t winX = ac->display.width - winW - 40;
|
||||
int32_t winY = 40;
|
||||
int32_t winW = CLOCK_WIN_W;
|
||||
int32_t winH = CLOCK_WIN_H;
|
||||
int32_t winX = ac->display.width - winW - CLOCK_MARGIN;
|
||||
int32_t winY = CLOCK_MARGIN;
|
||||
|
||||
// resizable=false: clock has a fixed size, no resize handle
|
||||
sWin = dvxCreateWindow(ac, "Clock", winX, winY, winW, winH, false);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,23 @@
|
|||
// Constants
|
||||
// ============================================================
|
||||
|
||||
#define CP_WIN_W 460
|
||||
#define CP_WIN_H 340
|
||||
#define WPAPER_DIR "CONFIG/WPAPER"
|
||||
#define CP_WIN_W 460
|
||||
#define CP_WIN_H 340
|
||||
#define CP_SPACING 8
|
||||
#define CP_SPACING_SMALL 4
|
||||
#define CP_THEME_BTN_W 60
|
||||
#define CP_SAVE_BTN_W 70
|
||||
#define CP_SWATCH_W 64
|
||||
#define CP_SWATCH_H 24
|
||||
#define CP_DBLCLICK_LBL_W 52
|
||||
#define CP_TEST_BTN_W 100
|
||||
#define CP_APPLY_BTN_W 100
|
||||
#define CP_OK_CANCEL_BTN_W 80
|
||||
#define CP_CONFIRM_W 300
|
||||
#define CP_CONFIRM_H 100
|
||||
#define CP_CONFIRM_BTN_W 70
|
||||
#define CP_WPAPER_BTN_W 90
|
||||
#define WPAPER_DIR "CONFIG/WPAPER"
|
||||
#define THEME_DIR "CONFIG/THEMES"
|
||||
#define THEME_EXT ".THM"
|
||||
|
||||
|
|
@ -156,12 +170,12 @@ static void buildColorsTab(WidgetT *page) {
|
|||
// Color list on the left, sliders on the right
|
||||
WidgetT *hbox = wgtHBox(page);
|
||||
hbox->weight = 100;
|
||||
hbox->spacing = wgtPixels(8);
|
||||
hbox->spacing = wgtPixels(CP_SPACING);
|
||||
|
||||
// Left side: color name list + theme controls
|
||||
WidgetT *leftVbox = wgtVBox(hbox);
|
||||
leftVbox->weight = 50;
|
||||
leftVbox->spacing = wgtPixels(4);
|
||||
leftVbox->spacing = wgtPixels(CP_SPACING_SMALL);
|
||||
|
||||
wgtLabel(leftVbox, "System Colors:");
|
||||
|
||||
|
|
@ -180,7 +194,7 @@ static void buildColorsTab(WidgetT *page) {
|
|||
|
||||
// Theme row
|
||||
WidgetT *themeRow = wgtHBox(leftVbox);
|
||||
themeRow->spacing = wgtPixels(4);
|
||||
themeRow->spacing = wgtPixels(CP_SPACING_SMALL);
|
||||
|
||||
wgtLabel(themeRow, "Theme:");
|
||||
|
||||
|
|
@ -191,19 +205,19 @@ static void buildColorsTab(WidgetT *page) {
|
|||
|
||||
WidgetT *loadBtn = wgtButton(themeRow, "Apply");
|
||||
loadBtn->onClick = onApplyTheme;
|
||||
loadBtn->prefW = wgtPixels(60);
|
||||
loadBtn->prefW = wgtPixels(CP_THEME_BTN_W);
|
||||
|
||||
WidgetT *browseBtn = wgtButton(themeRow, "Load...");
|
||||
browseBtn->onClick = onBrowseTheme;
|
||||
browseBtn->prefW = wgtPixels(60);
|
||||
browseBtn->prefW = wgtPixels(CP_THEME_BTN_W);
|
||||
|
||||
WidgetT *saveBtn = wgtButton(themeRow, "Save As...");
|
||||
saveBtn->onClick = onSaveTheme;
|
||||
saveBtn->prefW = wgtPixels(70);
|
||||
saveBtn->prefW = wgtPixels(CP_SAVE_BTN_W);
|
||||
|
||||
WidgetT *resetBtn = wgtButton(themeRow, "Reset");
|
||||
resetBtn->onClick = onResetColors;
|
||||
resetBtn->prefW = wgtPixels(60);
|
||||
resetBtn->prefW = wgtPixels(CP_THEME_BTN_W);
|
||||
|
||||
// Right side: RGB sliders in a non-weighted inner box so they stay
|
||||
// at natural size. The outer box absorbs extra vertical space.
|
||||
|
|
@ -211,7 +225,7 @@ static void buildColorsTab(WidgetT *page) {
|
|||
rightVbox->weight = 50;
|
||||
|
||||
WidgetT *sliderBox = wgtVBox(rightVbox);
|
||||
sliderBox->spacing = wgtPixels(4);
|
||||
sliderBox->spacing = wgtPixels(CP_SPACING_SMALL);
|
||||
|
||||
wgtLabel(sliderBox, "Red:");
|
||||
sRedSldr = wgtSlider(sliderBox, 0, 255);
|
||||
|
|
@ -232,7 +246,7 @@ static void buildColorsTab(WidgetT *page) {
|
|||
wgtLabelSetAlign(sBlueLbl, AlignEndE);
|
||||
|
||||
wgtLabel(sliderBox, "Preview:");
|
||||
sColorSwatch = wgtCanvas(sliderBox, 64, 24);
|
||||
sColorSwatch = wgtCanvas(sliderBox, CP_SWATCH_W, CP_SWATCH_H);
|
||||
|
||||
// Absorb remaining vertical space below the sliders
|
||||
wgtSpacer(rightVbox)->weight = 100;
|
||||
|
|
@ -257,19 +271,19 @@ static void buildDesktopTab(WidgetT *page) {
|
|||
sWallpaperLbl = wgtLabel(page, sWallpaperPath[0] ? sWallpaperPath : "(none)");
|
||||
|
||||
WidgetT *btnRow = wgtHBox(page);
|
||||
btnRow->spacing = wgtPixels(8);
|
||||
btnRow->spacing = wgtPixels(CP_SPACING);
|
||||
|
||||
WidgetT *applyBtn = wgtButton(btnRow, "Apply");
|
||||
applyBtn->onClick = onApplyWallpaper;
|
||||
applyBtn->prefW = wgtPixels(90);
|
||||
applyBtn->prefW = wgtPixels(CP_WPAPER_BTN_W);
|
||||
|
||||
WidgetT *chooseBtn = wgtButton(btnRow, "Browse...");
|
||||
chooseBtn->onClick = onChooseWallpaper;
|
||||
chooseBtn->prefW = wgtPixels(90);
|
||||
chooseBtn->prefW = wgtPixels(CP_WPAPER_BTN_W);
|
||||
|
||||
WidgetT *clearBtn = wgtButton(btnRow, "Clear");
|
||||
clearBtn->onClick = onClearWallpaper;
|
||||
clearBtn->prefW = wgtPixels(90);
|
||||
clearBtn->prefW = wgtPixels(CP_WPAPER_BTN_W);
|
||||
|
||||
wgtSpacer(btnRow)->weight = 100;
|
||||
wgtLabel(btnRow, "Mode:");
|
||||
|
|
@ -291,7 +305,7 @@ static void buildMouseTab(WidgetT *page) {
|
|||
|
||||
// Scroll direction
|
||||
WidgetT *wheelRow = wgtHBox(page);
|
||||
wheelRow->spacing = wgtPixels(8);
|
||||
wheelRow->spacing = wgtPixels(CP_SPACING);
|
||||
wgtLabel(wheelRow, "Scroll Wheel:");
|
||||
|
||||
static const char *wheelItems[] = {"Normal", "Reversed"};
|
||||
|
|
@ -301,13 +315,13 @@ static void buildMouseTab(WidgetT *page) {
|
|||
wgtDropdownSetItems(sWheelDrop, wheelItems, 2);
|
||||
wgtDropdownSetSelected(sWheelDrop, sAc->wheelDirection < 0 ? 1 : 0);
|
||||
|
||||
wgtSpacer(page)->prefH = wgtPixels(4);
|
||||
wgtSpacer(page)->prefH = wgtPixels(CP_SPACING_SMALL);
|
||||
|
||||
// Double-click speed
|
||||
wgtLabel(page, "Double-Click Speed:");
|
||||
|
||||
WidgetT *dblRow = wgtHBox(page);
|
||||
dblRow->spacing = wgtPixels(8);
|
||||
dblRow->spacing = wgtPixels(CP_SPACING);
|
||||
|
||||
wgtLabel(dblRow, "Fast");
|
||||
sDblClickSldr = wgtSlider(dblRow, 200, 900);
|
||||
|
|
@ -318,14 +332,14 @@ static void buildMouseTab(WidgetT *page) {
|
|||
wgtSliderSetValue(sDblClickSldr, dblMs);
|
||||
wgtLabel(dblRow, "Slow ");
|
||||
sDblClickLbl = wgtLabel(dblRow, "");
|
||||
sDblClickLbl->prefW = wgtPixels(52);
|
||||
sDblClickLbl->prefW = wgtPixels(CP_DBLCLICK_LBL_W);
|
||||
updateDblClickLabel();
|
||||
|
||||
wgtSpacer(page)->prefH = wgtPixels(4);
|
||||
wgtSpacer(page)->prefH = wgtPixels(CP_SPACING_SMALL);
|
||||
|
||||
// Acceleration
|
||||
WidgetT *accelRow = wgtHBox(page);
|
||||
accelRow->spacing = wgtPixels(8);
|
||||
accelRow->spacing = wgtPixels(CP_SPACING);
|
||||
wgtLabel(accelRow, "Acceleration:");
|
||||
|
||||
static const char *accelItems[] = {"Off", "Low", "Medium", "High"};
|
||||
|
|
@ -346,15 +360,15 @@ static void buildMouseTab(WidgetT *page) {
|
|||
wgtDropdownSetSelected(sAccelDrop, 2);
|
||||
}
|
||||
|
||||
wgtSpacer(page)->prefH = wgtPixels(4);
|
||||
wgtSpacer(page)->prefH = wgtPixels(CP_SPACING_SMALL);
|
||||
|
||||
// Double-click test area
|
||||
WidgetT *testRow = wgtHBox(page);
|
||||
testRow->spacing = wgtPixels(8);
|
||||
testRow->spacing = wgtPixels(CP_SPACING);
|
||||
|
||||
WidgetT *testBtn = wgtButton(testRow, "Test Area");
|
||||
testBtn->onDblClick = onDblClickTest;
|
||||
testBtn->prefW = wgtPixels(100);
|
||||
testBtn->prefW = wgtPixels(CP_TEST_BTN_W);
|
||||
|
||||
sDblTestLbl = wgtLabel(testRow, "Double-click the button to test");
|
||||
}
|
||||
|
|
@ -410,7 +424,7 @@ static void buildVideoTab(WidgetT *page) {
|
|||
|
||||
WidgetT *applyBtn = wgtButton(page, "Apply Mode");
|
||||
applyBtn->onClick = onVideoApply;
|
||||
applyBtn->prefW = wgtPixels(100);
|
||||
applyBtn->prefW = wgtPixels(CP_APPLY_BTN_W);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -710,7 +724,7 @@ static void onVideoApply(WidgetT *w) {
|
|||
// clicks Yes the mode is kept; No or timeout reverts to the old mode.
|
||||
#define CONFIRM_SECONDS 10
|
||||
|
||||
WindowT *confirmWin = dvxCreateWindowCentered(sAc, "Keep this mode?", 300, 100, false);
|
||||
WindowT *confirmWin = dvxCreateWindowCentered(sAc, "Keep this mode?", CP_CONFIRM_W, CP_CONFIRM_H, false);
|
||||
|
||||
if (!confirmWin) {
|
||||
dvxChangeVideoMode(sAc, oldW, oldH, oldBpp);
|
||||
|
|
@ -723,7 +737,7 @@ static void onVideoApply(WidgetT *w) {
|
|||
sAc->modalWindow = confirmWin;
|
||||
|
||||
WidgetT *root = wgtInitWindow(sAc, confirmWin);
|
||||
root->spacing = wgtPixels(8);
|
||||
root->spacing = wgtPixels(CP_SPACING);
|
||||
|
||||
static char msgBuf[80];
|
||||
snprintf(msgBuf, sizeof(msgBuf), "Reverting in %d seconds...", CONFIRM_SECONDS);
|
||||
|
|
@ -731,14 +745,14 @@ static void onVideoApply(WidgetT *w) {
|
|||
|
||||
WidgetT *btnRow = wgtHBox(root);
|
||||
btnRow->align = AlignEndE;
|
||||
btnRow->spacing = wgtPixels(8);
|
||||
btnRow->spacing = wgtPixels(CP_SPACING);
|
||||
|
||||
WidgetT *yesBtn = wgtButton(btnRow, "Yes");
|
||||
yesBtn->prefW = wgtPixels(70);
|
||||
yesBtn->prefW = wgtPixels(CP_CONFIRM_BTN_W);
|
||||
yesBtn->onClick = onVideoConfirmYes;
|
||||
|
||||
WidgetT *noBtn = wgtButton(btnRow, "No");
|
||||
noBtn->prefW = wgtPixels(70);
|
||||
noBtn->prefW = wgtPixels(CP_CONFIRM_BTN_W);
|
||||
noBtn->onClick = onVideoConfirmNo;
|
||||
|
||||
dvxFitWindow(sAc, confirmWin);
|
||||
|
|
@ -849,7 +863,7 @@ static void saveSnapshot(void) {
|
|||
memcpy(sSavedColorRgb, sAc->colorRgb, sizeof(sSavedColorRgb));
|
||||
sSavedWheelDir = sAc->wheelDirection;
|
||||
sSavedDblClick = (int32_t)(sAc->dblClickTicks * 1000 / CLOCKS_PER_SEC);
|
||||
sSavedAccel = 0;
|
||||
sSavedAccel = wgtDropdownGetSelected(sAccelDrop);
|
||||
sSavedVideoW = sAc->display.width;
|
||||
sSavedVideoH = sAc->display.height;
|
||||
sSavedVideoBpp = sAc->display.format.bitsPerPixel;
|
||||
|
|
@ -867,8 +881,8 @@ static void restoreSnapshot(void) {
|
|||
dvxApplyColorScheme(sAc);
|
||||
|
||||
// Restore mouse
|
||||
const char *accelStr = prefsGetString("mouse", "acceleration", "medium");
|
||||
int32_t accelVal = mapAccelName(accelStr);
|
||||
const char *accelName = mapAccelValue(sSavedAccel);
|
||||
int32_t accelVal = mapAccelName(accelName);
|
||||
dvxSetMouseConfig(sAc, sSavedWheelDir, sSavedDblClick, accelVal);
|
||||
|
||||
// Restore video mode if changed
|
||||
|
|
@ -1083,15 +1097,15 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
// OK / Cancel buttons at bottom
|
||||
WidgetT *btnRow = wgtHBox(root);
|
||||
btnRow->align = AlignEndE;
|
||||
btnRow->spacing = wgtPixels(8);
|
||||
btnRow->spacing = wgtPixels(CP_SPACING);
|
||||
|
||||
WidgetT *okBtn = wgtButton(btnRow, "OK");
|
||||
okBtn->onClick = onOk;
|
||||
okBtn->prefW = wgtPixels(80);
|
||||
okBtn->prefW = wgtPixels(CP_OK_CANCEL_BTN_W);
|
||||
|
||||
WidgetT *cancelBtn = wgtButton(btnRow, "Cancel");
|
||||
cancelBtn->onClick = onCancel;
|
||||
cancelBtn->prefW = wgtPixels(80);
|
||||
cancelBtn->prefW = wgtPixels(CP_OK_CANCEL_BTN_W);
|
||||
|
||||
dvxFitWindow(sAc, sWin);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -175,10 +175,8 @@ static void buildScaled(int32_t fitW, int32_t fitH) {
|
|||
|
||||
static void loadAndDisplay(const char *path) {
|
||||
// Free previous image
|
||||
if (sImgRgb) {
|
||||
stbi_image_free(sImgRgb);
|
||||
sImgRgb = NULL;
|
||||
}
|
||||
stbi_image_free(sImgRgb);
|
||||
sImgRgb = NULL;
|
||||
|
||||
int32_t channels;
|
||||
sImgRgb = stbi_load(path, &sImgW, &sImgH, &channels, 3);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@
|
|||
#define CMD_PASTE 202
|
||||
#define CMD_SELALL 203
|
||||
|
||||
#define NP_WIN_W 480
|
||||
#define NP_WIN_H 360
|
||||
#define NP_CASCADE_OFFSET 20
|
||||
|
||||
// ============================================================
|
||||
// Module state
|
||||
// ============================================================
|
||||
|
|
@ -333,10 +337,10 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
|
||||
int32_t screenW = ac->display.width;
|
||||
int32_t screenH = ac->display.height;
|
||||
int32_t winW = 480;
|
||||
int32_t winH = 360;
|
||||
int32_t winX = (screenW - winW) / 2 + 20;
|
||||
int32_t winY = (screenH - winH) / 3 + 20;
|
||||
int32_t winW = NP_WIN_W;
|
||||
int32_t winH = NP_WIN_H;
|
||||
int32_t winX = (screenW - winW) / 2 + NP_CASCADE_OFFSET;
|
||||
int32_t winY = (screenH - winH) / 3 + NP_CASCADE_OFFSET;
|
||||
|
||||
sWin = dvxCreateWindow(ac, "Untitled - Notepad", winX, winY, winW, winH, true);
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,11 @@
|
|||
#define PM_GRID_COLS 4
|
||||
#define PM_BTN_W 100
|
||||
#define PM_BTN_H 24
|
||||
#define PM_WIN_W 440
|
||||
#define PM_WIN_H 340
|
||||
#define PM_GRID_SPACING 8
|
||||
#define PM_SYSINFO_WIN_W 400
|
||||
#define PM_SYSINFO_WIN_H 360
|
||||
|
||||
// Menu command IDs
|
||||
#define CMD_RUN 100
|
||||
|
|
@ -124,8 +129,8 @@ AppDescriptorT appDescriptor = {
|
|||
static void buildPmWindow(void) {
|
||||
int32_t screenW = sAc->display.width;
|
||||
int32_t screenH = sAc->display.height;
|
||||
int32_t winW = 440;
|
||||
int32_t winH = 340;
|
||||
int32_t winW = PM_WIN_W;
|
||||
int32_t winH = PM_WIN_H;
|
||||
int32_t winX = (screenW - winW) / 2;
|
||||
int32_t winY = (screenH - winH) / 4;
|
||||
|
||||
|
|
@ -170,20 +175,17 @@ static void buildPmWindow(void) {
|
|||
appFrame->weight = 100;
|
||||
|
||||
if (sAppCount == 0) {
|
||||
WidgetT *lbl = wgtLabel(appFrame, "(No applications found in apps/ directory)");
|
||||
(void)lbl;
|
||||
wgtLabel(appFrame, "(No applications found in apps/ directory)");
|
||||
} else {
|
||||
// Build rows of buttons. Each row is an HBox holding PM_GRID_COLS
|
||||
// buttons. userData points back to the AppEntryT so the click
|
||||
// callback knows which app to launch.
|
||||
int32_t row = 0;
|
||||
WidgetT *hbox = NULL;
|
||||
|
||||
for (int32_t i = 0; i < sAppCount; i++) {
|
||||
if (i % PM_GRID_COLS == 0) {
|
||||
hbox = wgtHBox(appFrame);
|
||||
hbox->spacing = wgtPixels(8);
|
||||
row++;
|
||||
hbox->spacing = wgtPixels(PM_GRID_SPACING);
|
||||
}
|
||||
|
||||
WidgetT *btn = wgtButton(hbox, sAppFiles[i].name);
|
||||
|
|
@ -194,7 +196,6 @@ static void buildPmWindow(void) {
|
|||
btn->onClick = onAppButtonClick;
|
||||
}
|
||||
|
||||
(void)row;
|
||||
}
|
||||
|
||||
// Status bar at bottom; weight=100 on the label makes it fill the bar
|
||||
|
|
@ -420,8 +421,8 @@ static void showSystemInfo(void) {
|
|||
// Create a window with a read-only text area
|
||||
int32_t screenW = sAc->display.width;
|
||||
int32_t screenH = sAc->display.height;
|
||||
int32_t winW = 400;
|
||||
int32_t winH = 360;
|
||||
int32_t winW = PM_SYSINFO_WIN_W;
|
||||
int32_t winH = PM_SYSINFO_WIN_H;
|
||||
int32_t winX = (screenW - winW) / 2;
|
||||
int32_t winY = (screenH - winH) / 4;
|
||||
|
||||
|
|
|
|||
284
dvx/dvxApp.c
284
dvx/dvxApp.c
|
|
@ -67,6 +67,27 @@
|
|||
#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
|
||||
|
||||
// Cursor dirty region: covers all cursor shapes (16x16 with hotspot at 0,0 or 7,7)
|
||||
#define CURSOR_HOTSPOT_X 7
|
||||
#define CURSOR_HOTSPOT_Y 7
|
||||
#define CURSOR_DIRTY_SIZE 23
|
||||
|
||||
// 16.16 fixed-point constants for bilinear wallpaper scaling
|
||||
#define FP_ONE 65536
|
||||
#define FP_FRAC_SHIFT 8 // shift to extract 8-bit fractional part
|
||||
#define FP_FRAC_MASK 0xFF // mask for 8-bit fractional part
|
||||
#define FP_BLEND_MAX 256 // 1.0 in 8-bit fractional domain
|
||||
|
||||
// Wallpaper scaler yields to the event loop every N rows
|
||||
#define WALLPAPER_YIELD_ROWS 32
|
||||
|
||||
// Cascade windows default to 2/3 of screen dimensions
|
||||
#define CASCADE_SIZE_NUMER 2
|
||||
#define CASCADE_SIZE_DENOM 3
|
||||
|
||||
// RGB pixel stride (bytes per pixel in 24-bit RGB)
|
||||
#define RGB_CHANNELS 3
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
|
@ -81,7 +102,9 @@ static void closeSysMenu(AppContextT *ctx);
|
|||
static void interactiveScreenshot(AppContextT *ctx);
|
||||
static void interactiveWindowScreenshot(AppContextT *ctx, WindowT *win);
|
||||
static void compositeAndFlush(AppContextT *ctx);
|
||||
static int32_t countVisibleWindows(const AppContextT *ctx);
|
||||
static void dirtyCursorArea(AppContextT *ctx, int32_t x, int32_t y);
|
||||
static void ditherRgb(int32_t *r, int32_t *g, int32_t *b, int32_t row, int32_t col);
|
||||
static bool dispatchAccelKey(AppContextT *ctx, char key);
|
||||
static void dispatchEvents(AppContextT *ctx);
|
||||
static void drawCursorAt(AppContextT *ctx, int32_t x, int32_t y);
|
||||
|
|
@ -91,6 +114,7 @@ static WindowT *findWindowById(AppContextT *ctx, int32_t id);
|
|||
static void handleMouseButton(AppContextT *ctx, int32_t mx, int32_t my, int32_t buttons);
|
||||
static void enumModeCb(int32_t w, int32_t h, int32_t bpp, void *userData);
|
||||
static void initColorScheme(AppContextT *ctx);
|
||||
static void invalidateAllWindows(AppContextT *ctx);
|
||||
static void openContextMenu(AppContextT *ctx, WindowT *win, MenuT *menu, int32_t screenX, int32_t screenY);
|
||||
static void openPopupAtMenu(AppContextT *ctx, WindowT *win, int32_t menuIdx);
|
||||
static void openSubMenu(AppContextT *ctx);
|
||||
|
|
@ -111,6 +135,15 @@ static void updateTooltip(AppContextT *ctx);
|
|||
// keyboard activation was registered, matching Win3.x/Motif behavior.
|
||||
WidgetT *sKeyPressedBtn = NULL;
|
||||
|
||||
// 4x4 Bayer dithering offsets for ordered dithering when converting
|
||||
// 24-bit wallpaper images to 15/16-bit pixel formats.
|
||||
static const int32_t sBayerMatrix[4][4] = {
|
||||
{ -7, 1, -5, 3},
|
||||
{ 5, -3, 7, -1},
|
||||
{ -4, 4, -6, 2},
|
||||
{ 6, -2, 8, 0}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ============================================================
|
||||
|
|
@ -124,7 +157,7 @@ WidgetT *sKeyPressedBtn = NULL;
|
|||
// The 8bpp path uses the VGA palette for lookup.
|
||||
|
||||
static uint8_t *bufferToRgb(const DisplayT *d, const uint8_t *buf, int32_t w, int32_t h, int32_t pitch) {
|
||||
uint8_t *rgb = (uint8_t *)malloc((size_t)w * h * 3);
|
||||
uint8_t *rgb = (uint8_t *)malloc((size_t)w * h * RGB_CHANNELS);
|
||||
|
||||
if (!rgb) {
|
||||
return NULL;
|
||||
|
|
@ -149,9 +182,9 @@ static uint8_t *bufferToRgb(const DisplayT *d, const uint8_t *buf, int32_t w, in
|
|||
|
||||
if (d->format.bitsPerPixel == 8) {
|
||||
int32_t idx = pixel & 0xFF;
|
||||
dst[0] = d->palette[idx * 3 + 0];
|
||||
dst[1] = d->palette[idx * 3 + 1];
|
||||
dst[2] = d->palette[idx * 3 + 2];
|
||||
dst[0] = d->palette[idx * RGB_CHANNELS + 0];
|
||||
dst[1] = d->palette[idx * RGB_CHANNELS + 1];
|
||||
dst[2] = d->palette[idx * RGB_CHANNELS + 2];
|
||||
} else {
|
||||
uint32_t rv = (pixel >> d->format.redShift) & ((1u << d->format.redBits) - 1);
|
||||
uint32_t gv = (pixel >> d->format.greenShift) & ((1u << d->format.greenBits) - 1);
|
||||
|
|
@ -161,7 +194,7 @@ static uint8_t *bufferToRgb(const DisplayT *d, const uint8_t *buf, int32_t w, in
|
|||
dst[2] = (uint8_t)(bv << (8 - d->format.blueBits));
|
||||
}
|
||||
|
||||
dst += 3;
|
||||
dst += RGB_CHANNELS;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -580,6 +613,25 @@ static void compositeAndFlush(AppContextT *ctx) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// countVisibleWindows -- count non-minimized visible windows
|
||||
// ============================================================
|
||||
|
||||
static int32_t countVisibleWindows(const AppContextT *ctx) {
|
||||
int32_t count = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (!win->minimized && win->visible) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dirtyCursorArea
|
||||
// ============================================================
|
||||
|
|
@ -593,7 +645,37 @@ static void compositeAndFlush(AppContextT *ctx) {
|
|||
// cursor footprints (16x16 with hotspot at 0,0 or 7,7).
|
||||
|
||||
static void dirtyCursorArea(AppContextT *ctx, int32_t x, int32_t y) {
|
||||
dirtyListAdd(&ctx->dirty, x - 7, y - 7, 23, 23);
|
||||
dirtyListAdd(&ctx->dirty, x - CURSOR_HOTSPOT_X, y - CURSOR_HOTSPOT_Y, CURSOR_DIRTY_SIZE, CURSOR_DIRTY_SIZE);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// ditherRgb -- apply Bayer ordered dithering to an RGB triplet
|
||||
// ============================================================
|
||||
|
||||
static void ditherRgb(int32_t *r, int32_t *g, int32_t *b, int32_t row, int32_t col) {
|
||||
int32_t d = sBayerMatrix[row & 3][col & 3];
|
||||
*r += d;
|
||||
*g += d;
|
||||
*b += d;
|
||||
|
||||
if (*r < 0) {
|
||||
*r = 0;
|
||||
} else if (*r > 255) {
|
||||
*r = 255;
|
||||
}
|
||||
|
||||
if (*g < 0) {
|
||||
*g = 0;
|
||||
} else if (*g > 255) {
|
||||
*g = 255;
|
||||
}
|
||||
|
||||
if (*b < 0) {
|
||||
*b = 0;
|
||||
} else if (*b > 255) {
|
||||
*b = 255;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1597,19 +1679,7 @@ void dvxApplyColorScheme(AppContextT *ctx) {
|
|||
ctx->cursorFg = ctx->colors.cursorFg;
|
||||
ctx->cursorBg = ctx->colors.cursorBg;
|
||||
|
||||
// Repaint all windows with new colors
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (win->widgetRoot) {
|
||||
wgtInvalidate(win->widgetRoot);
|
||||
} else {
|
||||
dvxInvalidateWindow(ctx, win);
|
||||
}
|
||||
}
|
||||
|
||||
// Repaint desktop area too (wallpaper/background between windows)
|
||||
dirtyListAdd(&ctx->dirty, 0, 0, d->width, d->height);
|
||||
invalidateAllWindows(ctx);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1641,8 +1711,8 @@ void dvxCascadeWindows(AppContextT *ctx) {
|
|||
int32_t offsetY = 0;
|
||||
int32_t step = CHROME_TITLE_HEIGHT + CHROME_BORDER_WIDTH;
|
||||
|
||||
int32_t winW = screenW * 2 / 3;
|
||||
int32_t winH = screenH * 2 / 3;
|
||||
int32_t winW = screenW * CASCADE_SIZE_NUMER / CASCADE_SIZE_DENOM;
|
||||
int32_t winH = screenH * CASCADE_SIZE_NUMER / CASCADE_SIZE_DENOM;
|
||||
|
||||
if (winW < MIN_WINDOW_W) {
|
||||
winW = MIN_WINDOW_W;
|
||||
|
|
@ -2124,13 +2194,7 @@ void dvxSetColor(AppContextT *ctx, ColorIdE id, uint8_t r, uint8_t g, uint8_t b)
|
|||
ctx->cursorBg = packed;
|
||||
}
|
||||
|
||||
// Invalidate all windows so scrollbar/chrome changes are visible
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
win->contentDirty = true;
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, 0, 0, ctx->display.width, ctx->display.height);
|
||||
invalidateAllWindows(ctx);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2157,14 +2221,8 @@ static uint8_t *buildWallpaperBuf(AppContextT *ctx, const uint8_t *rgb, int32_t
|
|||
int32_t bpp = ctx->display.format.bitsPerPixel;
|
||||
int32_t bytesPerPx = ctx->display.format.bytesPerPixel;
|
||||
int32_t pitch = screenW * bytesPerPx;
|
||||
int32_t srcStride = imgW * 3;
|
||||
int32_t srcStride = imgW * RGB_CHANNELS;
|
||||
|
||||
static const int32_t bayerMatrix[4][4] = {
|
||||
{ -7, 1, -5, 3},
|
||||
{ 5, -3, 7, -1},
|
||||
{ -4, 4, -6, 2},
|
||||
{ 6, -2, 8, 0}
|
||||
};
|
||||
bool dither = (bpp == 15 || bpp == 16);
|
||||
|
||||
uint8_t *buf = (uint8_t *)malloc(pitch * screenH);
|
||||
|
|
@ -2177,61 +2235,78 @@ static uint8_t *buildWallpaperBuf(AppContextT *ctx, const uint8_t *rgb, int32_t
|
|||
// for the border area, but also ensures no garbage pixels)
|
||||
uint32_t bgPx = ctx->colors.desktop;
|
||||
|
||||
for (int32_t y = 0; y < screenH; y++) {
|
||||
uint8_t *dst = buf + y * pitch;
|
||||
|
||||
for (int32_t x = 0; x < screenW; x++) {
|
||||
writePixel(dst, x, bgPx, bpp);
|
||||
if (bytesPerPx == 1) {
|
||||
memset(buf, bgPx & 0xFF, pitch * screenH);
|
||||
} else if (bytesPerPx == 2) {
|
||||
uint16_t px16 = (uint16_t)bgPx;
|
||||
uint32_t fill32 = ((uint32_t)px16 << 16) | px16;
|
||||
for (int32_t y = 0; y < screenH; y++) {
|
||||
uint32_t *row = (uint32_t *)(buf + y * pitch);
|
||||
int32_t pairs = screenW / 2;
|
||||
for (int32_t i = 0; i < pairs; i++) {
|
||||
row[i] = fill32;
|
||||
}
|
||||
if (screenW & 1) {
|
||||
*(uint16_t *)(buf + y * pitch + (screenW - 1) * 2) = px16;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint32_t *row32 = (uint32_t *)buf;
|
||||
for (int32_t i = 0; i < pitch * screenH / 4; i++) {
|
||||
row32[i] = bgPx;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == WallpaperStretchE) {
|
||||
// Bilinear scale to screen dimensions with optional dither
|
||||
for (int32_t y = 0; y < screenH; y++) {
|
||||
if ((y & 31) == 0 && y > 0) {
|
||||
if ((y & (WALLPAPER_YIELD_ROWS - 1)) == 0 && y > 0) {
|
||||
dvxUpdate(ctx);
|
||||
}
|
||||
|
||||
int32_t srcYfp = (int32_t)((int64_t)y * imgH * 65536 / screenH);
|
||||
int32_t srcYfp = (int32_t)((int64_t)y * imgH * FP_ONE / screenH);
|
||||
int32_t sy0 = srcYfp >> 16;
|
||||
int32_t sy1 = (sy0 + 1 < imgH) ? sy0 + 1 : imgH - 1;
|
||||
int32_t fy = (srcYfp >> 8) & 0xFF;
|
||||
int32_t ify = 256 - fy;
|
||||
int32_t fy = (srcYfp >> FP_FRAC_SHIFT) & FP_FRAC_MASK;
|
||||
int32_t ify = FP_BLEND_MAX - fy;
|
||||
uint8_t *dst = buf + y * pitch;
|
||||
const uint8_t *row0 = rgb + sy0 * srcStride;
|
||||
const uint8_t *row1 = rgb + sy1 * srcStride;
|
||||
|
||||
for (int32_t x = 0; x < screenW; x++) {
|
||||
int32_t srcXfp = (int32_t)((int64_t)x * imgW * 65536 / screenW);
|
||||
int32_t srcXfp = (int32_t)((int64_t)x * imgW * FP_ONE / screenW);
|
||||
int32_t sx0 = srcXfp >> 16;
|
||||
int32_t sx1 = (sx0 + 1 < imgW) ? sx0 + 1 : imgW - 1;
|
||||
int32_t fx = (srcXfp >> 8) & 0xFF;
|
||||
int32_t ifx = 256 - fx;
|
||||
int32_t fx = (srcXfp >> FP_FRAC_SHIFT) & FP_FRAC_MASK;
|
||||
int32_t ifx = FP_BLEND_MAX - fx;
|
||||
|
||||
const uint8_t *p00 = row0 + sx0 * 3;
|
||||
const uint8_t *p10 = row0 + sx1 * 3;
|
||||
const uint8_t *p01 = row1 + sx0 * 3;
|
||||
const uint8_t *p11 = row1 + sx1 * 3;
|
||||
const uint8_t *p00 = row0 + sx0 * RGB_CHANNELS;
|
||||
const uint8_t *p10 = row0 + sx1 * RGB_CHANNELS;
|
||||
const uint8_t *p01 = row1 + sx0 * RGB_CHANNELS;
|
||||
const uint8_t *p11 = row1 + sx1 * RGB_CHANNELS;
|
||||
|
||||
int32_t r = (p00[0] * ifx * ify + p10[0] * fx * ify + p01[0] * ifx * fy + p11[0] * fx * fy) >> 16;
|
||||
int32_t g = (p00[1] * ifx * ify + p10[1] * fx * ify + p01[1] * ifx * fy + p11[1] * fx * fy) >> 16;
|
||||
int32_t b = (p00[2] * ifx * ify + p10[2] * fx * ify + p01[2] * ifx * fy + p11[2] * fx * fy) >> 16;
|
||||
|
||||
if (dither) {
|
||||
int32_t d = bayerMatrix[y & 3][x & 3];
|
||||
r += d; g += d; b += d;
|
||||
if (r < 0) r = 0; if (r > 255) r = 255;
|
||||
if (g < 0) g = 0; if (g > 255) g = 255;
|
||||
if (b < 0) b = 0; if (b > 255) b = 255;
|
||||
ditherRgb(&r, &g, &b, y, x);
|
||||
}
|
||||
|
||||
writePixel(dst, x, packColor(&ctx->display, (uint8_t)r, (uint8_t)g, (uint8_t)b), bpp);
|
||||
uint32_t px = packColor(&ctx->display, (uint8_t)r, (uint8_t)g, (uint8_t)b);
|
||||
if (bytesPerPx == 2) {
|
||||
((uint16_t *)dst)[x] = (uint16_t)px;
|
||||
} else if (bytesPerPx == 4) {
|
||||
((uint32_t *)dst)[x] = px;
|
||||
} else {
|
||||
dst[x] = (uint8_t)px;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mode == WallpaperTileE) {
|
||||
// Tile: repeat the image at native size across the screen
|
||||
for (int32_t y = 0; y < screenH; y++) {
|
||||
if ((y & 31) == 0 && y > 0) {
|
||||
if ((y & (WALLPAPER_YIELD_ROWS - 1)) == 0 && y > 0) {
|
||||
dvxUpdate(ctx);
|
||||
}
|
||||
|
||||
|
|
@ -2241,21 +2316,24 @@ static uint8_t *buildWallpaperBuf(AppContextT *ctx, const uint8_t *rgb, int32_t
|
|||
|
||||
for (int32_t x = 0; x < screenW; x++) {
|
||||
int32_t srcX = x % imgW;
|
||||
const uint8_t *src = srcRow + srcX * 3;
|
||||
const uint8_t *src = srcRow + srcX * RGB_CHANNELS;
|
||||
|
||||
int32_t r = src[0];
|
||||
int32_t g = src[1];
|
||||
int32_t b = src[2];
|
||||
|
||||
if (dither) {
|
||||
int32_t d = bayerMatrix[y & 3][x & 3];
|
||||
r += d; g += d; b += d;
|
||||
if (r < 0) r = 0; if (r > 255) r = 255;
|
||||
if (g < 0) g = 0; if (g > 255) g = 255;
|
||||
if (b < 0) b = 0; if (b > 255) b = 255;
|
||||
ditherRgb(&r, &g, &b, y, x);
|
||||
}
|
||||
|
||||
writePixel(dst, x, packColor(&ctx->display, (uint8_t)r, (uint8_t)g, (uint8_t)b), bpp);
|
||||
uint32_t px = packColor(&ctx->display, (uint8_t)r, (uint8_t)g, (uint8_t)b);
|
||||
if (bytesPerPx == 2) {
|
||||
((uint16_t *)dst)[x] = (uint16_t)px;
|
||||
} else if (bytesPerPx == 4) {
|
||||
((uint32_t *)dst)[x] = px;
|
||||
} else {
|
||||
dst[x] = (uint8_t)px;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -2279,7 +2357,7 @@ static uint8_t *buildWallpaperBuf(AppContextT *ctx, const uint8_t *rgb, int32_t
|
|||
}
|
||||
|
||||
for (int32_t sy = srcStartY; sy < srcEndY; sy++) {
|
||||
if (((sy - srcStartY) & 31) == 0 && sy > srcStartY) {
|
||||
if (((sy - srcStartY) & (WALLPAPER_YIELD_ROWS - 1)) == 0 && sy > srcStartY) {
|
||||
dvxUpdate(ctx);
|
||||
}
|
||||
|
||||
|
|
@ -2290,20 +2368,23 @@ static uint8_t *buildWallpaperBuf(AppContextT *ctx, const uint8_t *rgb, int32_t
|
|||
for (int32_t sx = srcStartX; sx < srcEndX; sx++) {
|
||||
int32_t dx = offX + sx;
|
||||
|
||||
const uint8_t *src = srcRow + sx * 3;
|
||||
const uint8_t *src = srcRow + sx * RGB_CHANNELS;
|
||||
int32_t r = src[0];
|
||||
int32_t g = src[1];
|
||||
int32_t b = src[2];
|
||||
|
||||
if (dither) {
|
||||
int32_t d = bayerMatrix[dy & 3][dx & 3];
|
||||
r += d; g += d; b += d;
|
||||
if (r < 0) r = 0; if (r > 255) r = 255;
|
||||
if (g < 0) g = 0; if (g > 255) g = 255;
|
||||
if (b < 0) b = 0; if (b > 255) b = 255;
|
||||
ditherRgb(&r, &g, &b, dy, dx);
|
||||
}
|
||||
|
||||
writePixel(dst, dx, packColor(&ctx->display, (uint8_t)r, (uint8_t)g, (uint8_t)b), bpp);
|
||||
uint32_t px = packColor(&ctx->display, (uint8_t)r, (uint8_t)g, (uint8_t)b);
|
||||
if (bytesPerPx == 2) {
|
||||
((uint16_t *)dst)[dx] = (uint16_t)px;
|
||||
} else if (bytesPerPx == 4) {
|
||||
((uint32_t *)dst)[dx] = px;
|
||||
} else {
|
||||
dst[dx] = (uint8_t)px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2430,7 +2511,7 @@ int32_t dvxInit(AppContextT *ctx, int32_t requestedW, int32_t requestedH, int32_
|
|||
// popup menu item index calculation can use multiply+shift instead
|
||||
// of division. On a 486, integer divide is 40+ cycles; this
|
||||
// reciprocal trick reduces it to ~10 cycles (imul + shr).
|
||||
ctx->charHeightRecip = ((uint32_t)0x10000 + (uint32_t)ctx->font.charHeight - 1) / (uint32_t)ctx->font.charHeight;
|
||||
ctx->charHeightRecip = ((uint32_t)FP_ONE + (uint32_t)ctx->font.charHeight - 1) / (uint32_t)ctx->font.charHeight;
|
||||
|
||||
// Dirty the entire screen so the first compositeAndFlush paints everything
|
||||
dirtyListAdd(&ctx->dirty, 0, 0, ctx->display.width, ctx->display.height);
|
||||
|
|
@ -2476,7 +2557,7 @@ uint8_t *dvxLoadImage(const AppContextT *ctx, const char *path, int32_t *outW, i
|
|||
|
||||
for (int32_t y = 0; y < imgH; y++) {
|
||||
for (int32_t x = 0; x < imgW; x++) {
|
||||
const uint8_t *src = rgb + (y * imgW + x) * 3;
|
||||
const uint8_t *src = rgb + (y * imgW + x) * RGB_CHANNELS;
|
||||
uint32_t color = packColor(d, src[0], src[1], src[2]);
|
||||
uint8_t *dst = buf + y * pitch + x * bpp;
|
||||
|
||||
|
|
@ -2673,7 +2754,7 @@ int32_t dvxSaveImage(const AppContextT *ctx, const uint8_t *data, int32_t w, int
|
|||
return -1;
|
||||
}
|
||||
|
||||
int32_t result = stbi_write_png(path, w, h, 3, rgb, w * 3) ? 0 : -1;
|
||||
int32_t result = stbi_write_png(path, w, h, RGB_CHANNELS, rgb, w * RGB_CHANNELS) ? 0 : -1;
|
||||
|
||||
free(rgb);
|
||||
|
||||
|
|
@ -2699,7 +2780,7 @@ int32_t dvxScreenshot(AppContextT *ctx, const char *path) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
int32_t result = stbi_write_png(path, d->width, d->height, 3, rgb, d->width * 3) ? 0 : -1;
|
||||
int32_t result = stbi_write_png(path, d->width, d->height, RGB_CHANNELS, rgb, d->width * RGB_CHANNELS) ? 0 : -1;
|
||||
|
||||
free(rgb);
|
||||
|
||||
|
|
@ -2784,7 +2865,7 @@ int32_t dvxWindowScreenshot(AppContextT *ctx, WindowT *win, const char *path) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
int32_t result = stbi_write_png(path, win->contentW, win->contentH, 3, rgb, win->contentW * 3) ? 0 : -1;
|
||||
int32_t result = stbi_write_png(path, win->contentW, win->contentH, RGB_CHANNELS, rgb, win->contentW * RGB_CHANNELS) ? 0 : -1;
|
||||
|
||||
free(rgb);
|
||||
|
||||
|
|
@ -2809,17 +2890,7 @@ int32_t dvxWindowScreenshot(AppContextT *ctx, WindowT *win, const char *path) {
|
|||
void dvxTileWindows(AppContextT *ctx) {
|
||||
int32_t screenW = ctx->display.width;
|
||||
int32_t screenH = ctx->display.height;
|
||||
|
||||
// Count eligible windows
|
||||
int32_t count = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (!win->minimized && win->visible) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
int32_t count = countVisibleWindows(ctx);
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
|
|
@ -2880,17 +2951,7 @@ void dvxTileWindows(AppContextT *ctx) {
|
|||
void dvxTileWindowsH(AppContextT *ctx) {
|
||||
int32_t screenW = ctx->display.width;
|
||||
int32_t screenH = ctx->display.height;
|
||||
|
||||
// Count eligible windows
|
||||
int32_t count = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (!win->minimized && win->visible) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
int32_t count = countVisibleWindows(ctx);
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
|
|
@ -2928,17 +2989,7 @@ void dvxTileWindowsH(AppContextT *ctx) {
|
|||
void dvxTileWindowsV(AppContextT *ctx) {
|
||||
int32_t screenW = ctx->display.width;
|
||||
int32_t screenH = ctx->display.height;
|
||||
|
||||
// Count eligible windows
|
||||
int32_t count = 0;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
WindowT *win = ctx->stack.windows[i];
|
||||
|
||||
if (!win->minimized && win->visible) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
int32_t count = countVisibleWindows(ctx);
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
|
|
@ -3325,6 +3376,17 @@ static void initColorScheme(AppContextT *ctx) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// invalidateAllWindows -- repaint all windows and desktop
|
||||
// ============================================================
|
||||
|
||||
static void invalidateAllWindows(AppContextT *ctx) {
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
dvxInvalidateWindow(ctx, ctx->stack.windows[i]);
|
||||
}
|
||||
|
||||
dirtyListAdd(&ctx->dirty, 0, 0, ctx->display.width, ctx->display.height);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -521,6 +521,9 @@ typedef struct WidgetT {
|
|||
struct WidgetT *dragItem; // item being dragged (NULL = none)
|
||||
struct WidgetT *dropTarget; // insertion target (NULL = none)
|
||||
bool dropAfter; // true = insert after target, false = before
|
||||
int32_t cachedTotalHeight; // cached result of calcTreeItemsHeight
|
||||
int32_t cachedMaxWidth; // cached result of calcTreeItemsMaxWidth
|
||||
bool dimsValid; // false = recalculate on next scrollbar check
|
||||
} treeView;
|
||||
|
||||
struct {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,14 @@
|
|||
#include <sys/nearptr.h>
|
||||
#include <sys/farptr.h>
|
||||
|
||||
// VESA mode scoring weights: higher score = preferred mode
|
||||
#define MODE_SCORE_16BPP 100 // 16bpp: fastest span fill (half the bytes of 32bpp)
|
||||
#define MODE_SCORE_15BPP 90 // 15bpp: slightly below 16 (some BIOSes conflate them)
|
||||
#define MODE_SCORE_32BPP 85 // 32bpp: true color but slower bus traffic
|
||||
#define MODE_SCORE_8BPP 70 // 8bpp: palette management adds complexity
|
||||
#define MODE_SCORE_PREF_BPP 20 // bonus if bpp matches user preference
|
||||
#define MODE_SCORE_EXACT_RES 10 // bonus/penalty for exact/oversize resolution
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
|
@ -376,25 +384,25 @@ static void getModeInfo(uint16_t mode, DisplayT *d, int32_t *score, int32_t requ
|
|||
int32_t s = 0;
|
||||
|
||||
if (bpp == 16) {
|
||||
s = 100;
|
||||
s = MODE_SCORE_16BPP;
|
||||
} else if (bpp == 15) {
|
||||
s = 90;
|
||||
s = MODE_SCORE_15BPP;
|
||||
} else if (bpp == 32) {
|
||||
s = 85;
|
||||
s = MODE_SCORE_32BPP;
|
||||
} else if (bpp == 8) {
|
||||
s = 70;
|
||||
s = MODE_SCORE_8BPP;
|
||||
}
|
||||
|
||||
// Prefer the user's preferred bpp
|
||||
if (bpp == preferredBpp) {
|
||||
s += 20;
|
||||
s += MODE_SCORE_PREF_BPP;
|
||||
}
|
||||
|
||||
// Exact resolution match is preferred
|
||||
if (w == requestedW && h == requestedH) {
|
||||
s += 10;
|
||||
s += MODE_SCORE_EXACT_RES;
|
||||
} else {
|
||||
s -= 10;
|
||||
s -= MODE_SCORE_EXACT_RES;
|
||||
}
|
||||
|
||||
*score = s;
|
||||
|
|
|
|||
|
|
@ -84,17 +84,7 @@ void wgtComboBoxSetItems(WidgetT *w, const char **items, int32_t count) {
|
|||
// Cache max item string length so calcMinSize doesn't need to re-scan
|
||||
// the entire item array on every layout pass. Items are stored as
|
||||
// external pointers (not copied) -- the caller owns the string data.
|
||||
int32_t maxLen = 0;
|
||||
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
int32_t slen = (int32_t)strlen(items[i]);
|
||||
|
||||
if (slen > maxLen) {
|
||||
maxLen = slen;
|
||||
}
|
||||
}
|
||||
|
||||
w->as.comboBox.maxItemLen = maxLen;
|
||||
w->as.comboBox.maxItemLen = widgetMaxItemLen(items, count);
|
||||
|
||||
if (w->as.comboBox.selectedIdx >= count) {
|
||||
w->as.comboBox.selectedIdx = -1;
|
||||
|
|
@ -346,10 +336,7 @@ void widgetComboBoxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
|||
uint32_t arrowFg = w->enabled ? colors->contentFg : colors->windowShadow;
|
||||
int32_t arrowX = w->x + textAreaW + DROPDOWN_BTN_WIDTH / 2;
|
||||
int32_t arrowY = w->y + w->h / 2 - 1;
|
||||
|
||||
for (int32_t i = 0; i < 4; i++) {
|
||||
drawHLine(d, ops, arrowX - 3 + i, arrowY + i, 7 - i * 2, arrowFg);
|
||||
}
|
||||
widgetDrawDropdownArrow(d, ops, arrowX, arrowY, arrowFg);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,21 @@ void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t conten
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetDrawDropdownArrow
|
||||
// ============================================================
|
||||
//
|
||||
// Draws a small downward-pointing filled triangle (7, 5, 3, 1 pixels
|
||||
// wide across 4 rows) centered at the given position. Used by both
|
||||
// Dropdown and ComboBox for the drop button arrow glyph.
|
||||
|
||||
void widgetDrawDropdownArrow(DisplayT *d, const BlitOpsT *ops, int32_t centerX, int32_t centerY, uint32_t color) {
|
||||
for (int32_t i = 0; i < 4; i++) {
|
||||
drawHLine(d, ops, centerX - 3 + i, centerY + i, 7 - i * 2, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetFindByAccel
|
||||
// ============================================================
|
||||
|
|
@ -542,6 +557,29 @@ bool widgetIsHorizContainer(WidgetTypeE type) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetMaxItemLen
|
||||
// ============================================================
|
||||
//
|
||||
// Scans an array of string items and returns the maximum strlen.
|
||||
// Shared by ListBox, Dropdown, and ComboBox to cache the widest
|
||||
// item length for calcMinSize without duplicating the loop.
|
||||
|
||||
int32_t widgetMaxItemLen(const char **items, int32_t count) {
|
||||
int32_t maxLen = 0;
|
||||
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
int32_t slen = (int32_t)strlen(items[i]);
|
||||
|
||||
if (slen > maxLen) {
|
||||
maxLen = slen;
|
||||
}
|
||||
}
|
||||
|
||||
return maxLen;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetNavigateIndex
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -59,17 +59,7 @@ void wgtDropdownSetItems(WidgetT *w, const char **items, int32_t count) {
|
|||
w->as.dropdown.itemCount = count;
|
||||
|
||||
// Cache max item strlen to avoid recomputing in calcMinSize
|
||||
int32_t maxLen = 0;
|
||||
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
int32_t slen = (int32_t)strlen(items[i]);
|
||||
|
||||
if (slen > maxLen) {
|
||||
maxLen = slen;
|
||||
}
|
||||
}
|
||||
|
||||
w->as.dropdown.maxItemLen = maxLen;
|
||||
w->as.dropdown.maxItemLen = widgetMaxItemLen(items, count);
|
||||
|
||||
if (w->as.dropdown.selectedIdx >= count) {
|
||||
w->as.dropdown.selectedIdx = -1;
|
||||
|
|
@ -254,16 +244,11 @@ void widgetDropdownPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
|||
btnBevel.width = 2;
|
||||
drawBevel(d, ops, w->x + textAreaW, w->y, DROPDOWN_BTN_WIDTH, w->h, &btnBevel);
|
||||
|
||||
// Down arrow glyph in button -- a small filled triangle drawn as horizontal
|
||||
// lines of decreasing width (7, 5, 3, 1 pixels). This creates a 4-pixel
|
||||
// tall downward-pointing triangle centered in the button.
|
||||
// Down arrow glyph in button
|
||||
uint32_t arrowFg = w->enabled ? colors->contentFg : colors->windowShadow;
|
||||
int32_t arrowX = w->x + textAreaW + DROPDOWN_BTN_WIDTH / 2;
|
||||
int32_t arrowY = w->y + w->h / 2 - 1;
|
||||
|
||||
for (int32_t i = 0; i < 4; i++) {
|
||||
drawHLine(d, ops, arrowX - 3 + i, arrowY + i, 7 - i * 2, arrowFg);
|
||||
}
|
||||
widgetDrawDropdownArrow(d, ops, arrowX, arrowY, arrowFg);
|
||||
|
||||
if (w->focused) {
|
||||
drawFocusRect(d, ops, w->x + 3, w->y + 3, textAreaW - 6, w->h - 6, fg);
|
||||
|
|
|
|||
|
|
@ -925,6 +925,8 @@ void widgetReorderDrop(WidgetT *w) {
|
|||
return;
|
||||
}
|
||||
|
||||
w->as.treeView.dimsValid = false;
|
||||
|
||||
// Unlink drag from its current parent
|
||||
WidgetT *oldParent = drag->parent;
|
||||
|
||||
|
|
|
|||
|
|
@ -237,6 +237,12 @@ int32_t widgetNavigateIndex(int32_t key, int32_t current, int32_t count, int32_t
|
|||
// Compute popup position for a dropdown/combobox, clamped to screen bounds.
|
||||
void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH);
|
||||
|
||||
// Draw the small downward-pointing triangle glyph used by dropdown buttons.
|
||||
void widgetDrawDropdownArrow(DisplayT *d, const BlitOpsT *ops, int32_t centerX, int32_t centerY, uint32_t color);
|
||||
|
||||
// Compute the maximum strlen across an array of string items.
|
||||
int32_t widgetMaxItemLen(const char **items, int32_t count);
|
||||
|
||||
// Paint a generic popup item list (used by dropdown, combobox, and popup menus).
|
||||
void widgetPaintPopupList(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t popX, int32_t popY, int32_t popW, int32_t popH, const char **items, int32_t itemCount, int32_t hoverIdx, int32_t scrollPos);
|
||||
|
||||
|
|
|
|||
|
|
@ -238,17 +238,7 @@ void wgtListBoxSetItems(WidgetT *w, const char **items, int32_t count) {
|
|||
w->as.listBox.itemCount = count;
|
||||
|
||||
// Cache max item strlen to avoid recomputing in calcMinSize
|
||||
int32_t maxLen = 0;
|
||||
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
int32_t slen = (int32_t)strlen(items[i]);
|
||||
|
||||
if (slen > maxLen) {
|
||||
maxLen = slen;
|
||||
}
|
||||
}
|
||||
|
||||
w->as.listBox.maxItemLen = maxLen;
|
||||
w->as.listBox.maxItemLen = widgetMaxItemLen(items, count);
|
||||
|
||||
if (w->as.listBox.selectedIdx >= count) {
|
||||
w->as.listBox.selectedIdx = count > 0 ? 0 : -1;
|
||||
|
|
|
|||
|
|
@ -405,6 +405,16 @@ void wgtListViewSetColumns(WidgetT *w, const ListViewColT *cols, int32_t count)
|
|||
w->as.listView->cols = cols;
|
||||
w->as.listView->colCount = count;
|
||||
w->as.listView->totalColW = 0;
|
||||
|
||||
// Eagerly resolve column widths if data is already set
|
||||
if (w->as.listView->rowCount > 0) {
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
|
||||
if (ctx) {
|
||||
resolveColumnWidths(w, &ctx->font);
|
||||
}
|
||||
}
|
||||
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
|
@ -446,6 +456,15 @@ void wgtListViewSetData(WidgetT *w, const char **cellData, int32_t rowCount) {
|
|||
w->as.listView->selBits[w->as.listView->selectedIdx] = 1;
|
||||
}
|
||||
|
||||
// Eagerly resolve column widths if columns are defined
|
||||
if (w->as.listView->colCount > 0) {
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
|
||||
if (ctx) {
|
||||
resolveColumnWidths(w, &ctx->font);
|
||||
}
|
||||
}
|
||||
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,16 @@ void wgtDestroy(WidgetT *w) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Invalidate tree dimension cache if destroying a tree item
|
||||
if (w->type == WidgetTreeItemE && w->parent) {
|
||||
for (WidgetT *p = w->parent; p; p = p->parent) {
|
||||
if (p->type == WidgetTreeViewE) {
|
||||
p->as.treeView.dimsValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (w->parent) {
|
||||
widgetRemoveChild(w->parent, w);
|
||||
}
|
||||
|
|
@ -555,6 +565,17 @@ void wgtSetTooltip(WidgetT *w, const char *text) {
|
|||
void wgtSetVisible(WidgetT *w, bool visible) {
|
||||
if (w) {
|
||||
w->visible = visible;
|
||||
|
||||
// Invalidate tree dimension cache if this is a tree item
|
||||
if (w->type == WidgetTreeItemE && w->parent) {
|
||||
for (WidgetT *p = w->parent; p; p = p->parent) {
|
||||
if (p->type == WidgetTreeViewE) {
|
||||
p->as.treeView.dimsValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@
|
|||
// the actual node reparenting via widgetRemoveChild/widgetAddChild.
|
||||
//
|
||||
// Performance note: calcTreeItemsHeight and calcTreeItemsMaxWidth
|
||||
// walk the full visible tree on every call. For trees with hundreds
|
||||
// of items this could be optimized with caching (as TextArea does
|
||||
// for line count). In practice, DOS-era tree views are small enough
|
||||
// that the linear scan is fast on a Pentium.
|
||||
// walk the full visible tree, but results are cached in the TreeView's
|
||||
// cachedTotalHeight/cachedMaxWidth fields. The cache is invalidated
|
||||
// (dimsValid = false) whenever the tree structure changes: item add/
|
||||
// remove, expand/collapse, text change, or drag-reorder.
|
||||
|
||||
#include "widgetInternal.h"
|
||||
|
||||
|
|
@ -57,6 +57,7 @@ static int32_t calcTreeItemsHeight(WidgetT *parent, const BitmapFontT *font);
|
|||
static int32_t calcTreeItemsMaxWidth(WidgetT *parent, const BitmapFontT *font, int32_t depth);
|
||||
static void clearAllSelections(WidgetT *parent);
|
||||
static WidgetT *firstVisibleItem(WidgetT *treeView);
|
||||
static void invalidateTreeDims(WidgetT *w);
|
||||
static void layoutTreeItems(WidgetT *parent, const BitmapFontT *font, int32_t x, int32_t *y, int32_t width, int32_t depth);
|
||||
static WidgetT *nextVisibleItem(WidgetT *item, WidgetT *treeView);
|
||||
static void paintReorderIndicator(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t innerW);
|
||||
|
|
@ -167,6 +168,24 @@ static WidgetT *firstVisibleItem(WidgetT *treeView) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// invalidateTreeDims
|
||||
// ============================================================
|
||||
//
|
||||
// Walk up from any widget (typically a TreeItem) to find the ancestor
|
||||
// TreeView and mark its cached dimensions invalid. This ensures the
|
||||
// next treeCalcScrollbarNeeds call will recompute height and width.
|
||||
|
||||
static void invalidateTreeDims(WidgetT *w) {
|
||||
for (WidgetT *p = w; p; p = p->parent) {
|
||||
if (p->type == WidgetTreeViewE) {
|
||||
p->as.treeView.dimsValid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// layoutTreeItems
|
||||
// ============================================================
|
||||
|
|
@ -489,8 +508,14 @@ static void selectRange(WidgetT *treeView, WidgetT *from, WidgetT *to) {
|
|||
// needed, accounting for the mutual space dependency between them.
|
||||
|
||||
static void treeCalcScrollbarNeeds(WidgetT *w, const BitmapFontT *font, int32_t *outTotalH, int32_t *outTotalW, int32_t *outInnerH, int32_t *outInnerW, bool *outNeedVSb, bool *outNeedHSb) {
|
||||
int32_t totalH = calcTreeItemsHeight(w, font);
|
||||
int32_t totalW = calcTreeItemsMaxWidth(w, font, 0);
|
||||
if (!w->as.treeView.dimsValid) {
|
||||
w->as.treeView.cachedTotalHeight = calcTreeItemsHeight(w, font);
|
||||
w->as.treeView.cachedMaxWidth = calcTreeItemsMaxWidth(w, font, 0);
|
||||
w->as.treeView.dimsValid = true;
|
||||
}
|
||||
|
||||
int32_t totalH = w->as.treeView.cachedTotalHeight;
|
||||
int32_t totalW = w->as.treeView.cachedMaxWidth;
|
||||
int32_t innerH = w->h - TREE_BORDER * 2;
|
||||
int32_t innerW = w->w - TREE_BORDER * 2;
|
||||
bool needVSb = (totalH > innerH);
|
||||
|
|
@ -611,6 +636,7 @@ WidgetT *wgtTreeItem(WidgetT *parent, const char *text) {
|
|||
if (w) {
|
||||
w->as.treeItem.text = text;
|
||||
w->as.treeItem.expanded = false;
|
||||
invalidateTreeDims(w);
|
||||
}
|
||||
|
||||
return w;
|
||||
|
|
@ -632,6 +658,7 @@ const char *widgetTreeItemGetText(const WidgetT *w) {
|
|||
|
||||
void widgetTreeItemSetText(WidgetT *w, const char *text) {
|
||||
w->as.treeItem.text = text;
|
||||
invalidateTreeDims(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -654,6 +681,7 @@ void wgtTreeItemSetExpanded(WidgetT *w, bool expanded) {
|
|||
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
||||
|
||||
w->as.treeItem.expanded = expanded;
|
||||
invalidateTreeDims(w);
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
|
@ -682,6 +710,7 @@ static void treeDefaultDblClick(WidgetT *w) {
|
|||
|
||||
if (hasChildren) {
|
||||
sel->as.treeItem.expanded = !sel->as.treeItem.expanded;
|
||||
invalidateTreeDims(sel);
|
||||
|
||||
if (sel->onChange) {
|
||||
sel->onChange(sel);
|
||||
|
|
@ -875,6 +904,7 @@ void widgetTreeViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
if (hasChildren) {
|
||||
if (!sel->as.treeItem.expanded) {
|
||||
sel->as.treeItem.expanded = true;
|
||||
w->as.treeView.dimsValid = false;
|
||||
|
||||
if (sel->onChange) {
|
||||
sel->onChange(sel);
|
||||
|
|
@ -893,6 +923,7 @@ void widgetTreeViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
if (sel) {
|
||||
if (sel->as.treeItem.expanded) {
|
||||
sel->as.treeItem.expanded = false;
|
||||
w->as.treeView.dimsValid = false;
|
||||
|
||||
if (sel->onChange) {
|
||||
sel->onChange(sel);
|
||||
|
|
@ -915,6 +946,7 @@ void widgetTreeViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
|
||||
if (hasChildren) {
|
||||
sel->as.treeItem.expanded = !sel->as.treeItem.expanded;
|
||||
w->as.treeView.dimsValid = false;
|
||||
|
||||
if (sel->onChange) {
|
||||
sel->onChange(sel);
|
||||
|
|
@ -1218,6 +1250,7 @@ void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
|
|||
if (vx >= iconX && vx < iconX + TREE_EXPAND_SIZE) {
|
||||
clickedExpandIcon = true;
|
||||
item->as.treeItem.expanded = !item->as.treeItem.expanded;
|
||||
hit->as.treeView.dimsValid = false;
|
||||
|
||||
// Clamp scroll positions if collapsing reduced content size
|
||||
if (!item->as.treeItem.expanded) {
|
||||
|
|
|
|||
|
|
@ -21,17 +21,23 @@
|
|||
|
||||
#define TM_COL_COUNT 5
|
||||
#define TM_MAX_PATH 260
|
||||
#define TM_WIN_W 520
|
||||
#define TM_WIN_H 280
|
||||
#define TM_LIST_PREF_H 160
|
||||
#define TM_BTN_SPACING 8
|
||||
#define TM_BTN_W 90
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
static void onTmClose(WindowT *win);
|
||||
static void onTmEndTask(WidgetT *w);
|
||||
static void onTmRun(WidgetT *w);
|
||||
static void onTmSwitchTo(WidgetT *w);
|
||||
static void refreshTaskList(void);
|
||||
static void updateStatusText(void);
|
||||
static ShellAppT *getRunningAppByIndex(int32_t sel);
|
||||
static void onTmClose(WindowT *win);
|
||||
static void onTmEndTask(WidgetT *w);
|
||||
static void onTmRun(WidgetT *w);
|
||||
static void onTmSwitchTo(WidgetT *w);
|
||||
static void refreshTaskList(void);
|
||||
static void updateStatusText(void);
|
||||
|
||||
// ============================================================
|
||||
// Module state
|
||||
|
|
@ -52,6 +58,28 @@ static const char **sCells = NULL; // dynamic array of cell pointers
|
|||
static TmRowStringsT *sRowStrs = NULL; // dynamic array of per-row strings
|
||||
|
||||
|
||||
// ============================================================
|
||||
// getRunningAppByIndex
|
||||
// ============================================================
|
||||
|
||||
static ShellAppT *getRunningAppByIndex(int32_t sel) {
|
||||
int32_t idx = 0;
|
||||
|
||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
||||
ShellAppT *app = shellGetApp(i);
|
||||
|
||||
if (app && app->state == AppStateRunningE) {
|
||||
if (idx == sel) {
|
||||
return app;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// onTmClose
|
||||
// ============================================================
|
||||
|
|
@ -86,22 +114,11 @@ static void onTmEndTask(WidgetT *w) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Re-walk the app slot table in the same order as refreshTaskList()
|
||||
// to map the selected row index back to the correct ShellAppT.
|
||||
int32_t idx = 0;
|
||||
ShellAppT *app = getRunningAppByIndex(sel);
|
||||
|
||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
||||
ShellAppT *app = shellGetApp(i);
|
||||
|
||||
if (app && app->state == AppStateRunningE) {
|
||||
if (idx == sel) {
|
||||
shellForceKillApp(sCtx, app);
|
||||
shellDesktopUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
if (app) {
|
||||
shellForceKillApp(sCtx, app);
|
||||
shellDesktopUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,34 +159,25 @@ static void onTmSwitchTo(WidgetT *w) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Same index-to-appId mapping as refreshTaskList. Scan the window
|
||||
// stack top-down (highest Z first) to find the app's topmost window,
|
||||
// restore it if minimized, then raise and focus it.
|
||||
int32_t idx = 0;
|
||||
ShellAppT *app = getRunningAppByIndex(sel);
|
||||
|
||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
||||
ShellAppT *app = shellGetApp(i);
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (app && app->state == AppStateRunningE) {
|
||||
if (idx == sel) {
|
||||
for (int32_t j = sCtx->stack.count - 1; j >= 0; j--) {
|
||||
WindowT *win = sCtx->stack.windows[j];
|
||||
// Scan the window stack top-down (highest Z first) to find the app's
|
||||
// topmost window, restore it if minimized, then raise and focus it.
|
||||
for (int32_t j = sCtx->stack.count - 1; j >= 0; j--) {
|
||||
WindowT *win = sCtx->stack.windows[j];
|
||||
|
||||
if (win->appId == i) {
|
||||
if (win->minimized) {
|
||||
wmRestoreMinimized(&sCtx->stack, &sCtx->dirty, win);
|
||||
}
|
||||
|
||||
wmRaiseWindow(&sCtx->stack, &sCtx->dirty, j);
|
||||
wmSetFocus(&sCtx->stack, &sCtx->dirty, sCtx->stack.count - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
if (win->appId == app->appId) {
|
||||
if (win->minimized) {
|
||||
wmRestoreMinimized(&sCtx->stack, &sCtx->dirty, win);
|
||||
}
|
||||
|
||||
idx++;
|
||||
wmRaiseWindow(&sCtx->stack, &sCtx->dirty, j);
|
||||
wmSetFocus(&sCtx->stack, &sCtx->dirty, sCtx->stack.count - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -283,8 +291,8 @@ void shellTaskMgrOpen(AppContextT *ctx) {
|
|||
return;
|
||||
}
|
||||
|
||||
int32_t winW = 520;
|
||||
int32_t winH = 280;
|
||||
int32_t winW = TM_WIN_W;
|
||||
int32_t winH = TM_WIN_H;
|
||||
int32_t winX = (ctx->display.width - winW) / 2;
|
||||
int32_t winY = (ctx->display.height - winH) / 3;
|
||||
|
||||
|
|
@ -318,26 +326,26 @@ void shellTaskMgrOpen(AppContextT *ctx) {
|
|||
|
||||
sTmListView = wgtListView(root);
|
||||
sTmListView->weight = 100;
|
||||
sTmListView->prefH = wgtPixels(160);
|
||||
sTmListView->prefH = wgtPixels(TM_LIST_PREF_H);
|
||||
wgtListViewSetColumns(sTmListView, tmCols, TM_COL_COUNT);
|
||||
|
||||
WidgetT *btnRow = wgtHBox(root);
|
||||
btnRow->spacing = wgtPixels(8);
|
||||
btnRow->spacing = wgtPixels(TM_BTN_SPACING);
|
||||
|
||||
sTmStatusLbl = wgtLabel(btnRow, "");
|
||||
sTmStatusLbl->weight = 100;
|
||||
|
||||
WidgetT *switchBtn = wgtButton(btnRow, "Switch To");
|
||||
switchBtn->onClick = onTmSwitchTo;
|
||||
switchBtn->prefW = wgtPixels(90);
|
||||
switchBtn->prefW = wgtPixels(TM_BTN_W);
|
||||
|
||||
WidgetT *endBtn = wgtButton(btnRow, "End Task");
|
||||
endBtn->onClick = onTmEndTask;
|
||||
endBtn->prefW = wgtPixels(90);
|
||||
endBtn->prefW = wgtPixels(TM_BTN_W);
|
||||
|
||||
WidgetT *runBtn = wgtButton(btnRow, "Run...");
|
||||
runBtn->onClick = onTmRun;
|
||||
runBtn->prefW = wgtPixels(90);
|
||||
runBtn->prefW = wgtPixels(TM_BTN_W);
|
||||
|
||||
shellRegisterDesktopUpdate(shellTaskMgrRefresh);
|
||||
refreshTaskList();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue