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
|
// Module state
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
|
#define CLOCK_WIN_W 200
|
||||||
|
#define CLOCK_WIN_H 100
|
||||||
|
#define CLOCK_MARGIN 40
|
||||||
|
#define CLOCK_DATE_GAP 8
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool quit;
|
bool quit;
|
||||||
char timeStr[32];
|
char timeStr[32];
|
||||||
|
|
@ -125,7 +130,7 @@ static void onPaint(WindowT *win, RectT *dirty) {
|
||||||
// Date string (centered below)
|
// Date string (centered below)
|
||||||
int32_t dateW = textWidth(font, sState.dateStr);
|
int32_t dateW = textWidth(font, sState.dateStr);
|
||||||
int32_t dateX = (win->contentW - dateW) / 2;
|
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);
|
drawText(&cd, ops, font, dateX, dateY, sState.dateStr, colors->contentFg, colors->contentBg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,10 +187,10 @@ int32_t appMain(DxeAppContextT *ctx) {
|
||||||
updateTime();
|
updateTime();
|
||||||
|
|
||||||
// Position in the upper-right corner, out of the way of other windows
|
// Position in the upper-right corner, out of the way of other windows
|
||||||
int32_t winW = 200;
|
int32_t winW = CLOCK_WIN_W;
|
||||||
int32_t winH = 100;
|
int32_t winH = CLOCK_WIN_H;
|
||||||
int32_t winX = ac->display.width - winW - 40;
|
int32_t winX = ac->display.width - winW - CLOCK_MARGIN;
|
||||||
int32_t winY = 40;
|
int32_t winY = CLOCK_MARGIN;
|
||||||
|
|
||||||
// resizable=false: clock has a fixed size, no resize handle
|
// resizable=false: clock has a fixed size, no resize handle
|
||||||
sWin = dvxCreateWindow(ac, "Clock", winX, winY, winW, winH, false);
|
sWin = dvxCreateWindow(ac, "Clock", winX, winY, winW, winH, false);
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,20 @@
|
||||||
|
|
||||||
#define CP_WIN_W 460
|
#define CP_WIN_W 460
|
||||||
#define CP_WIN_H 340
|
#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 WPAPER_DIR "CONFIG/WPAPER"
|
||||||
#define THEME_DIR "CONFIG/THEMES"
|
#define THEME_DIR "CONFIG/THEMES"
|
||||||
#define THEME_EXT ".THM"
|
#define THEME_EXT ".THM"
|
||||||
|
|
@ -156,12 +170,12 @@ static void buildColorsTab(WidgetT *page) {
|
||||||
// Color list on the left, sliders on the right
|
// Color list on the left, sliders on the right
|
||||||
WidgetT *hbox = wgtHBox(page);
|
WidgetT *hbox = wgtHBox(page);
|
||||||
hbox->weight = 100;
|
hbox->weight = 100;
|
||||||
hbox->spacing = wgtPixels(8);
|
hbox->spacing = wgtPixels(CP_SPACING);
|
||||||
|
|
||||||
// Left side: color name list + theme controls
|
// Left side: color name list + theme controls
|
||||||
WidgetT *leftVbox = wgtVBox(hbox);
|
WidgetT *leftVbox = wgtVBox(hbox);
|
||||||
leftVbox->weight = 50;
|
leftVbox->weight = 50;
|
||||||
leftVbox->spacing = wgtPixels(4);
|
leftVbox->spacing = wgtPixels(CP_SPACING_SMALL);
|
||||||
|
|
||||||
wgtLabel(leftVbox, "System Colors:");
|
wgtLabel(leftVbox, "System Colors:");
|
||||||
|
|
||||||
|
|
@ -180,7 +194,7 @@ static void buildColorsTab(WidgetT *page) {
|
||||||
|
|
||||||
// Theme row
|
// Theme row
|
||||||
WidgetT *themeRow = wgtHBox(leftVbox);
|
WidgetT *themeRow = wgtHBox(leftVbox);
|
||||||
themeRow->spacing = wgtPixels(4);
|
themeRow->spacing = wgtPixels(CP_SPACING_SMALL);
|
||||||
|
|
||||||
wgtLabel(themeRow, "Theme:");
|
wgtLabel(themeRow, "Theme:");
|
||||||
|
|
||||||
|
|
@ -191,19 +205,19 @@ static void buildColorsTab(WidgetT *page) {
|
||||||
|
|
||||||
WidgetT *loadBtn = wgtButton(themeRow, "Apply");
|
WidgetT *loadBtn = wgtButton(themeRow, "Apply");
|
||||||
loadBtn->onClick = onApplyTheme;
|
loadBtn->onClick = onApplyTheme;
|
||||||
loadBtn->prefW = wgtPixels(60);
|
loadBtn->prefW = wgtPixels(CP_THEME_BTN_W);
|
||||||
|
|
||||||
WidgetT *browseBtn = wgtButton(themeRow, "Load...");
|
WidgetT *browseBtn = wgtButton(themeRow, "Load...");
|
||||||
browseBtn->onClick = onBrowseTheme;
|
browseBtn->onClick = onBrowseTheme;
|
||||||
browseBtn->prefW = wgtPixels(60);
|
browseBtn->prefW = wgtPixels(CP_THEME_BTN_W);
|
||||||
|
|
||||||
WidgetT *saveBtn = wgtButton(themeRow, "Save As...");
|
WidgetT *saveBtn = wgtButton(themeRow, "Save As...");
|
||||||
saveBtn->onClick = onSaveTheme;
|
saveBtn->onClick = onSaveTheme;
|
||||||
saveBtn->prefW = wgtPixels(70);
|
saveBtn->prefW = wgtPixels(CP_SAVE_BTN_W);
|
||||||
|
|
||||||
WidgetT *resetBtn = wgtButton(themeRow, "Reset");
|
WidgetT *resetBtn = wgtButton(themeRow, "Reset");
|
||||||
resetBtn->onClick = onResetColors;
|
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
|
// Right side: RGB sliders in a non-weighted inner box so they stay
|
||||||
// at natural size. The outer box absorbs extra vertical space.
|
// at natural size. The outer box absorbs extra vertical space.
|
||||||
|
|
@ -211,7 +225,7 @@ static void buildColorsTab(WidgetT *page) {
|
||||||
rightVbox->weight = 50;
|
rightVbox->weight = 50;
|
||||||
|
|
||||||
WidgetT *sliderBox = wgtVBox(rightVbox);
|
WidgetT *sliderBox = wgtVBox(rightVbox);
|
||||||
sliderBox->spacing = wgtPixels(4);
|
sliderBox->spacing = wgtPixels(CP_SPACING_SMALL);
|
||||||
|
|
||||||
wgtLabel(sliderBox, "Red:");
|
wgtLabel(sliderBox, "Red:");
|
||||||
sRedSldr = wgtSlider(sliderBox, 0, 255);
|
sRedSldr = wgtSlider(sliderBox, 0, 255);
|
||||||
|
|
@ -232,7 +246,7 @@ static void buildColorsTab(WidgetT *page) {
|
||||||
wgtLabelSetAlign(sBlueLbl, AlignEndE);
|
wgtLabelSetAlign(sBlueLbl, AlignEndE);
|
||||||
|
|
||||||
wgtLabel(sliderBox, "Preview:");
|
wgtLabel(sliderBox, "Preview:");
|
||||||
sColorSwatch = wgtCanvas(sliderBox, 64, 24);
|
sColorSwatch = wgtCanvas(sliderBox, CP_SWATCH_W, CP_SWATCH_H);
|
||||||
|
|
||||||
// Absorb remaining vertical space below the sliders
|
// Absorb remaining vertical space below the sliders
|
||||||
wgtSpacer(rightVbox)->weight = 100;
|
wgtSpacer(rightVbox)->weight = 100;
|
||||||
|
|
@ -257,19 +271,19 @@ static void buildDesktopTab(WidgetT *page) {
|
||||||
sWallpaperLbl = wgtLabel(page, sWallpaperPath[0] ? sWallpaperPath : "(none)");
|
sWallpaperLbl = wgtLabel(page, sWallpaperPath[0] ? sWallpaperPath : "(none)");
|
||||||
|
|
||||||
WidgetT *btnRow = wgtHBox(page);
|
WidgetT *btnRow = wgtHBox(page);
|
||||||
btnRow->spacing = wgtPixels(8);
|
btnRow->spacing = wgtPixels(CP_SPACING);
|
||||||
|
|
||||||
WidgetT *applyBtn = wgtButton(btnRow, "Apply");
|
WidgetT *applyBtn = wgtButton(btnRow, "Apply");
|
||||||
applyBtn->onClick = onApplyWallpaper;
|
applyBtn->onClick = onApplyWallpaper;
|
||||||
applyBtn->prefW = wgtPixels(90);
|
applyBtn->prefW = wgtPixels(CP_WPAPER_BTN_W);
|
||||||
|
|
||||||
WidgetT *chooseBtn = wgtButton(btnRow, "Browse...");
|
WidgetT *chooseBtn = wgtButton(btnRow, "Browse...");
|
||||||
chooseBtn->onClick = onChooseWallpaper;
|
chooseBtn->onClick = onChooseWallpaper;
|
||||||
chooseBtn->prefW = wgtPixels(90);
|
chooseBtn->prefW = wgtPixels(CP_WPAPER_BTN_W);
|
||||||
|
|
||||||
WidgetT *clearBtn = wgtButton(btnRow, "Clear");
|
WidgetT *clearBtn = wgtButton(btnRow, "Clear");
|
||||||
clearBtn->onClick = onClearWallpaper;
|
clearBtn->onClick = onClearWallpaper;
|
||||||
clearBtn->prefW = wgtPixels(90);
|
clearBtn->prefW = wgtPixels(CP_WPAPER_BTN_W);
|
||||||
|
|
||||||
wgtSpacer(btnRow)->weight = 100;
|
wgtSpacer(btnRow)->weight = 100;
|
||||||
wgtLabel(btnRow, "Mode:");
|
wgtLabel(btnRow, "Mode:");
|
||||||
|
|
@ -291,7 +305,7 @@ static void buildMouseTab(WidgetT *page) {
|
||||||
|
|
||||||
// Scroll direction
|
// Scroll direction
|
||||||
WidgetT *wheelRow = wgtHBox(page);
|
WidgetT *wheelRow = wgtHBox(page);
|
||||||
wheelRow->spacing = wgtPixels(8);
|
wheelRow->spacing = wgtPixels(CP_SPACING);
|
||||||
wgtLabel(wheelRow, "Scroll Wheel:");
|
wgtLabel(wheelRow, "Scroll Wheel:");
|
||||||
|
|
||||||
static const char *wheelItems[] = {"Normal", "Reversed"};
|
static const char *wheelItems[] = {"Normal", "Reversed"};
|
||||||
|
|
@ -301,13 +315,13 @@ static void buildMouseTab(WidgetT *page) {
|
||||||
wgtDropdownSetItems(sWheelDrop, wheelItems, 2);
|
wgtDropdownSetItems(sWheelDrop, wheelItems, 2);
|
||||||
wgtDropdownSetSelected(sWheelDrop, sAc->wheelDirection < 0 ? 1 : 0);
|
wgtDropdownSetSelected(sWheelDrop, sAc->wheelDirection < 0 ? 1 : 0);
|
||||||
|
|
||||||
wgtSpacer(page)->prefH = wgtPixels(4);
|
wgtSpacer(page)->prefH = wgtPixels(CP_SPACING_SMALL);
|
||||||
|
|
||||||
// Double-click speed
|
// Double-click speed
|
||||||
wgtLabel(page, "Double-Click Speed:");
|
wgtLabel(page, "Double-Click Speed:");
|
||||||
|
|
||||||
WidgetT *dblRow = wgtHBox(page);
|
WidgetT *dblRow = wgtHBox(page);
|
||||||
dblRow->spacing = wgtPixels(8);
|
dblRow->spacing = wgtPixels(CP_SPACING);
|
||||||
|
|
||||||
wgtLabel(dblRow, "Fast");
|
wgtLabel(dblRow, "Fast");
|
||||||
sDblClickSldr = wgtSlider(dblRow, 200, 900);
|
sDblClickSldr = wgtSlider(dblRow, 200, 900);
|
||||||
|
|
@ -318,14 +332,14 @@ static void buildMouseTab(WidgetT *page) {
|
||||||
wgtSliderSetValue(sDblClickSldr, dblMs);
|
wgtSliderSetValue(sDblClickSldr, dblMs);
|
||||||
wgtLabel(dblRow, "Slow ");
|
wgtLabel(dblRow, "Slow ");
|
||||||
sDblClickLbl = wgtLabel(dblRow, "");
|
sDblClickLbl = wgtLabel(dblRow, "");
|
||||||
sDblClickLbl->prefW = wgtPixels(52);
|
sDblClickLbl->prefW = wgtPixels(CP_DBLCLICK_LBL_W);
|
||||||
updateDblClickLabel();
|
updateDblClickLabel();
|
||||||
|
|
||||||
wgtSpacer(page)->prefH = wgtPixels(4);
|
wgtSpacer(page)->prefH = wgtPixels(CP_SPACING_SMALL);
|
||||||
|
|
||||||
// Acceleration
|
// Acceleration
|
||||||
WidgetT *accelRow = wgtHBox(page);
|
WidgetT *accelRow = wgtHBox(page);
|
||||||
accelRow->spacing = wgtPixels(8);
|
accelRow->spacing = wgtPixels(CP_SPACING);
|
||||||
wgtLabel(accelRow, "Acceleration:");
|
wgtLabel(accelRow, "Acceleration:");
|
||||||
|
|
||||||
static const char *accelItems[] = {"Off", "Low", "Medium", "High"};
|
static const char *accelItems[] = {"Off", "Low", "Medium", "High"};
|
||||||
|
|
@ -346,15 +360,15 @@ static void buildMouseTab(WidgetT *page) {
|
||||||
wgtDropdownSetSelected(sAccelDrop, 2);
|
wgtDropdownSetSelected(sAccelDrop, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
wgtSpacer(page)->prefH = wgtPixels(4);
|
wgtSpacer(page)->prefH = wgtPixels(CP_SPACING_SMALL);
|
||||||
|
|
||||||
// Double-click test area
|
// Double-click test area
|
||||||
WidgetT *testRow = wgtHBox(page);
|
WidgetT *testRow = wgtHBox(page);
|
||||||
testRow->spacing = wgtPixels(8);
|
testRow->spacing = wgtPixels(CP_SPACING);
|
||||||
|
|
||||||
WidgetT *testBtn = wgtButton(testRow, "Test Area");
|
WidgetT *testBtn = wgtButton(testRow, "Test Area");
|
||||||
testBtn->onDblClick = onDblClickTest;
|
testBtn->onDblClick = onDblClickTest;
|
||||||
testBtn->prefW = wgtPixels(100);
|
testBtn->prefW = wgtPixels(CP_TEST_BTN_W);
|
||||||
|
|
||||||
sDblTestLbl = wgtLabel(testRow, "Double-click the button to test");
|
sDblTestLbl = wgtLabel(testRow, "Double-click the button to test");
|
||||||
}
|
}
|
||||||
|
|
@ -410,7 +424,7 @@ static void buildVideoTab(WidgetT *page) {
|
||||||
|
|
||||||
WidgetT *applyBtn = wgtButton(page, "Apply Mode");
|
WidgetT *applyBtn = wgtButton(page, "Apply Mode");
|
||||||
applyBtn->onClick = onVideoApply;
|
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.
|
// clicks Yes the mode is kept; No or timeout reverts to the old mode.
|
||||||
#define CONFIRM_SECONDS 10
|
#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) {
|
if (!confirmWin) {
|
||||||
dvxChangeVideoMode(sAc, oldW, oldH, oldBpp);
|
dvxChangeVideoMode(sAc, oldW, oldH, oldBpp);
|
||||||
|
|
@ -723,7 +737,7 @@ static void onVideoApply(WidgetT *w) {
|
||||||
sAc->modalWindow = confirmWin;
|
sAc->modalWindow = confirmWin;
|
||||||
|
|
||||||
WidgetT *root = wgtInitWindow(sAc, confirmWin);
|
WidgetT *root = wgtInitWindow(sAc, confirmWin);
|
||||||
root->spacing = wgtPixels(8);
|
root->spacing = wgtPixels(CP_SPACING);
|
||||||
|
|
||||||
static char msgBuf[80];
|
static char msgBuf[80];
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "Reverting in %d seconds...", CONFIRM_SECONDS);
|
snprintf(msgBuf, sizeof(msgBuf), "Reverting in %d seconds...", CONFIRM_SECONDS);
|
||||||
|
|
@ -731,14 +745,14 @@ static void onVideoApply(WidgetT *w) {
|
||||||
|
|
||||||
WidgetT *btnRow = wgtHBox(root);
|
WidgetT *btnRow = wgtHBox(root);
|
||||||
btnRow->align = AlignEndE;
|
btnRow->align = AlignEndE;
|
||||||
btnRow->spacing = wgtPixels(8);
|
btnRow->spacing = wgtPixels(CP_SPACING);
|
||||||
|
|
||||||
WidgetT *yesBtn = wgtButton(btnRow, "Yes");
|
WidgetT *yesBtn = wgtButton(btnRow, "Yes");
|
||||||
yesBtn->prefW = wgtPixels(70);
|
yesBtn->prefW = wgtPixels(CP_CONFIRM_BTN_W);
|
||||||
yesBtn->onClick = onVideoConfirmYes;
|
yesBtn->onClick = onVideoConfirmYes;
|
||||||
|
|
||||||
WidgetT *noBtn = wgtButton(btnRow, "No");
|
WidgetT *noBtn = wgtButton(btnRow, "No");
|
||||||
noBtn->prefW = wgtPixels(70);
|
noBtn->prefW = wgtPixels(CP_CONFIRM_BTN_W);
|
||||||
noBtn->onClick = onVideoConfirmNo;
|
noBtn->onClick = onVideoConfirmNo;
|
||||||
|
|
||||||
dvxFitWindow(sAc, confirmWin);
|
dvxFitWindow(sAc, confirmWin);
|
||||||
|
|
@ -849,7 +863,7 @@ static void saveSnapshot(void) {
|
||||||
memcpy(sSavedColorRgb, sAc->colorRgb, sizeof(sSavedColorRgb));
|
memcpy(sSavedColorRgb, sAc->colorRgb, sizeof(sSavedColorRgb));
|
||||||
sSavedWheelDir = sAc->wheelDirection;
|
sSavedWheelDir = sAc->wheelDirection;
|
||||||
sSavedDblClick = (int32_t)(sAc->dblClickTicks * 1000 / CLOCKS_PER_SEC);
|
sSavedDblClick = (int32_t)(sAc->dblClickTicks * 1000 / CLOCKS_PER_SEC);
|
||||||
sSavedAccel = 0;
|
sSavedAccel = wgtDropdownGetSelected(sAccelDrop);
|
||||||
sSavedVideoW = sAc->display.width;
|
sSavedVideoW = sAc->display.width;
|
||||||
sSavedVideoH = sAc->display.height;
|
sSavedVideoH = sAc->display.height;
|
||||||
sSavedVideoBpp = sAc->display.format.bitsPerPixel;
|
sSavedVideoBpp = sAc->display.format.bitsPerPixel;
|
||||||
|
|
@ -867,8 +881,8 @@ static void restoreSnapshot(void) {
|
||||||
dvxApplyColorScheme(sAc);
|
dvxApplyColorScheme(sAc);
|
||||||
|
|
||||||
// Restore mouse
|
// Restore mouse
|
||||||
const char *accelStr = prefsGetString("mouse", "acceleration", "medium");
|
const char *accelName = mapAccelValue(sSavedAccel);
|
||||||
int32_t accelVal = mapAccelName(accelStr);
|
int32_t accelVal = mapAccelName(accelName);
|
||||||
dvxSetMouseConfig(sAc, sSavedWheelDir, sSavedDblClick, accelVal);
|
dvxSetMouseConfig(sAc, sSavedWheelDir, sSavedDblClick, accelVal);
|
||||||
|
|
||||||
// Restore video mode if changed
|
// Restore video mode if changed
|
||||||
|
|
@ -1083,15 +1097,15 @@ int32_t appMain(DxeAppContextT *ctx) {
|
||||||
// OK / Cancel buttons at bottom
|
// OK / Cancel buttons at bottom
|
||||||
WidgetT *btnRow = wgtHBox(root);
|
WidgetT *btnRow = wgtHBox(root);
|
||||||
btnRow->align = AlignEndE;
|
btnRow->align = AlignEndE;
|
||||||
btnRow->spacing = wgtPixels(8);
|
btnRow->spacing = wgtPixels(CP_SPACING);
|
||||||
|
|
||||||
WidgetT *okBtn = wgtButton(btnRow, "OK");
|
WidgetT *okBtn = wgtButton(btnRow, "OK");
|
||||||
okBtn->onClick = onOk;
|
okBtn->onClick = onOk;
|
||||||
okBtn->prefW = wgtPixels(80);
|
okBtn->prefW = wgtPixels(CP_OK_CANCEL_BTN_W);
|
||||||
|
|
||||||
WidgetT *cancelBtn = wgtButton(btnRow, "Cancel");
|
WidgetT *cancelBtn = wgtButton(btnRow, "Cancel");
|
||||||
cancelBtn->onClick = onCancel;
|
cancelBtn->onClick = onCancel;
|
||||||
cancelBtn->prefW = wgtPixels(80);
|
cancelBtn->prefW = wgtPixels(CP_OK_CANCEL_BTN_W);
|
||||||
|
|
||||||
dvxFitWindow(sAc, sWin);
|
dvxFitWindow(sAc, sWin);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -175,10 +175,8 @@ static void buildScaled(int32_t fitW, int32_t fitH) {
|
||||||
|
|
||||||
static void loadAndDisplay(const char *path) {
|
static void loadAndDisplay(const char *path) {
|
||||||
// Free previous image
|
// Free previous image
|
||||||
if (sImgRgb) {
|
|
||||||
stbi_image_free(sImgRgb);
|
stbi_image_free(sImgRgb);
|
||||||
sImgRgb = NULL;
|
sImgRgb = NULL;
|
||||||
}
|
|
||||||
|
|
||||||
int32_t channels;
|
int32_t channels;
|
||||||
sImgRgb = stbi_load(path, &sImgW, &sImgH, &channels, 3);
|
sImgRgb = stbi_load(path, &sImgW, &sImgH, &channels, 3);
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@
|
||||||
#define CMD_PASTE 202
|
#define CMD_PASTE 202
|
||||||
#define CMD_SELALL 203
|
#define CMD_SELALL 203
|
||||||
|
|
||||||
|
#define NP_WIN_W 480
|
||||||
|
#define NP_WIN_H 360
|
||||||
|
#define NP_CASCADE_OFFSET 20
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Module state
|
// Module state
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -333,10 +337,10 @@ int32_t appMain(DxeAppContextT *ctx) {
|
||||||
|
|
||||||
int32_t screenW = ac->display.width;
|
int32_t screenW = ac->display.width;
|
||||||
int32_t screenH = ac->display.height;
|
int32_t screenH = ac->display.height;
|
||||||
int32_t winW = 480;
|
int32_t winW = NP_WIN_W;
|
||||||
int32_t winH = 360;
|
int32_t winH = NP_WIN_H;
|
||||||
int32_t winX = (screenW - winW) / 2 + 20;
|
int32_t winX = (screenW - winW) / 2 + NP_CASCADE_OFFSET;
|
||||||
int32_t winY = (screenH - winH) / 3 + 20;
|
int32_t winY = (screenH - winH) / 3 + NP_CASCADE_OFFSET;
|
||||||
|
|
||||||
sWin = dvxCreateWindow(ac, "Untitled - Notepad", winX, winY, winW, winH, true);
|
sWin = dvxCreateWindow(ac, "Untitled - Notepad", winX, winY, winW, winH, true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,11 @@
|
||||||
#define PM_GRID_COLS 4
|
#define PM_GRID_COLS 4
|
||||||
#define PM_BTN_W 100
|
#define PM_BTN_W 100
|
||||||
#define PM_BTN_H 24
|
#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
|
// Menu command IDs
|
||||||
#define CMD_RUN 100
|
#define CMD_RUN 100
|
||||||
|
|
@ -124,8 +129,8 @@ AppDescriptorT appDescriptor = {
|
||||||
static void buildPmWindow(void) {
|
static void buildPmWindow(void) {
|
||||||
int32_t screenW = sAc->display.width;
|
int32_t screenW = sAc->display.width;
|
||||||
int32_t screenH = sAc->display.height;
|
int32_t screenH = sAc->display.height;
|
||||||
int32_t winW = 440;
|
int32_t winW = PM_WIN_W;
|
||||||
int32_t winH = 340;
|
int32_t winH = PM_WIN_H;
|
||||||
int32_t winX = (screenW - winW) / 2;
|
int32_t winX = (screenW - winW) / 2;
|
||||||
int32_t winY = (screenH - winH) / 4;
|
int32_t winY = (screenH - winH) / 4;
|
||||||
|
|
||||||
|
|
@ -170,20 +175,17 @@ static void buildPmWindow(void) {
|
||||||
appFrame->weight = 100;
|
appFrame->weight = 100;
|
||||||
|
|
||||||
if (sAppCount == 0) {
|
if (sAppCount == 0) {
|
||||||
WidgetT *lbl = wgtLabel(appFrame, "(No applications found in apps/ directory)");
|
wgtLabel(appFrame, "(No applications found in apps/ directory)");
|
||||||
(void)lbl;
|
|
||||||
} else {
|
} else {
|
||||||
// Build rows of buttons. Each row is an HBox holding PM_GRID_COLS
|
// Build rows of buttons. Each row is an HBox holding PM_GRID_COLS
|
||||||
// buttons. userData points back to the AppEntryT so the click
|
// buttons. userData points back to the AppEntryT so the click
|
||||||
// callback knows which app to launch.
|
// callback knows which app to launch.
|
||||||
int32_t row = 0;
|
|
||||||
WidgetT *hbox = NULL;
|
WidgetT *hbox = NULL;
|
||||||
|
|
||||||
for (int32_t i = 0; i < sAppCount; i++) {
|
for (int32_t i = 0; i < sAppCount; i++) {
|
||||||
if (i % PM_GRID_COLS == 0) {
|
if (i % PM_GRID_COLS == 0) {
|
||||||
hbox = wgtHBox(appFrame);
|
hbox = wgtHBox(appFrame);
|
||||||
hbox->spacing = wgtPixels(8);
|
hbox->spacing = wgtPixels(PM_GRID_SPACING);
|
||||||
row++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetT *btn = wgtButton(hbox, sAppFiles[i].name);
|
WidgetT *btn = wgtButton(hbox, sAppFiles[i].name);
|
||||||
|
|
@ -194,7 +196,6 @@ static void buildPmWindow(void) {
|
||||||
btn->onClick = onAppButtonClick;
|
btn->onClick = onAppButtonClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)row;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status bar at bottom; weight=100 on the label makes it fill the bar
|
// 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
|
// Create a window with a read-only text area
|
||||||
int32_t screenW = sAc->display.width;
|
int32_t screenW = sAc->display.width;
|
||||||
int32_t screenH = sAc->display.height;
|
int32_t screenH = sAc->display.height;
|
||||||
int32_t winW = 400;
|
int32_t winW = PM_SYSINFO_WIN_W;
|
||||||
int32_t winH = 360;
|
int32_t winH = PM_SYSINFO_WIN_H;
|
||||||
int32_t winX = (screenW - winW) / 2;
|
int32_t winX = (screenW - winW) / 2;
|
||||||
int32_t winY = (screenH - winH) / 4;
|
int32_t winY = (screenH - winH) / 4;
|
||||||
|
|
||||||
|
|
|
||||||
282
dvx/dvxApp.c
282
dvx/dvxApp.c
|
|
@ -67,6 +67,27 @@
|
||||||
#define POPUP_ITEM_PAD_H 8 // extra horizontal padding in popup items
|
#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
|
#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
|
// Prototypes
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -81,7 +102,9 @@ static void closeSysMenu(AppContextT *ctx);
|
||||||
static void interactiveScreenshot(AppContextT *ctx);
|
static void interactiveScreenshot(AppContextT *ctx);
|
||||||
static void interactiveWindowScreenshot(AppContextT *ctx, WindowT *win);
|
static void interactiveWindowScreenshot(AppContextT *ctx, WindowT *win);
|
||||||
static void compositeAndFlush(AppContextT *ctx);
|
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 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 bool dispatchAccelKey(AppContextT *ctx, char key);
|
||||||
static void dispatchEvents(AppContextT *ctx);
|
static void dispatchEvents(AppContextT *ctx);
|
||||||
static void drawCursorAt(AppContextT *ctx, int32_t x, int32_t y);
|
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 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 enumModeCb(int32_t w, int32_t h, int32_t bpp, void *userData);
|
||||||
static void initColorScheme(AppContextT *ctx);
|
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 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 openPopupAtMenu(AppContextT *ctx, WindowT *win, int32_t menuIdx);
|
||||||
static void openSubMenu(AppContextT *ctx);
|
static void openSubMenu(AppContextT *ctx);
|
||||||
|
|
@ -111,6 +135,15 @@ static void updateTooltip(AppContextT *ctx);
|
||||||
// keyboard activation was registered, matching Win3.x/Motif behavior.
|
// keyboard activation was registered, matching Win3.x/Motif behavior.
|
||||||
WidgetT *sKeyPressedBtn = NULL;
|
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.
|
// 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) {
|
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) {
|
if (!rgb) {
|
||||||
return NULL;
|
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) {
|
if (d->format.bitsPerPixel == 8) {
|
||||||
int32_t idx = pixel & 0xFF;
|
int32_t idx = pixel & 0xFF;
|
||||||
dst[0] = d->palette[idx * 3 + 0];
|
dst[0] = d->palette[idx * RGB_CHANNELS + 0];
|
||||||
dst[1] = d->palette[idx * 3 + 1];
|
dst[1] = d->palette[idx * RGB_CHANNELS + 1];
|
||||||
dst[2] = d->palette[idx * 3 + 2];
|
dst[2] = d->palette[idx * RGB_CHANNELS + 2];
|
||||||
} else {
|
} else {
|
||||||
uint32_t rv = (pixel >> d->format.redShift) & ((1u << d->format.redBits) - 1);
|
uint32_t rv = (pixel >> d->format.redShift) & ((1u << d->format.redBits) - 1);
|
||||||
uint32_t gv = (pixel >> d->format.greenShift) & ((1u << d->format.greenBits) - 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[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
|
// dirtyCursorArea
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -593,7 +645,37 @@ static void compositeAndFlush(AppContextT *ctx) {
|
||||||
// cursor footprints (16x16 with hotspot at 0,0 or 7,7).
|
// cursor footprints (16x16 with hotspot at 0,0 or 7,7).
|
||||||
|
|
||||||
static void dirtyCursorArea(AppContextT *ctx, int32_t x, int32_t y) {
|
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->cursorFg = ctx->colors.cursorFg;
|
||||||
ctx->cursorBg = ctx->colors.cursorBg;
|
ctx->cursorBg = ctx->colors.cursorBg;
|
||||||
|
|
||||||
// Repaint all windows with new colors
|
invalidateAllWindows(ctx);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1641,8 +1711,8 @@ void dvxCascadeWindows(AppContextT *ctx) {
|
||||||
int32_t offsetY = 0;
|
int32_t offsetY = 0;
|
||||||
int32_t step = CHROME_TITLE_HEIGHT + CHROME_BORDER_WIDTH;
|
int32_t step = CHROME_TITLE_HEIGHT + CHROME_BORDER_WIDTH;
|
||||||
|
|
||||||
int32_t winW = screenW * 2 / 3;
|
int32_t winW = screenW * CASCADE_SIZE_NUMER / CASCADE_SIZE_DENOM;
|
||||||
int32_t winH = screenH * 2 / 3;
|
int32_t winH = screenH * CASCADE_SIZE_NUMER / CASCADE_SIZE_DENOM;
|
||||||
|
|
||||||
if (winW < MIN_WINDOW_W) {
|
if (winW < MIN_WINDOW_W) {
|
||||||
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;
|
ctx->cursorBg = packed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate all windows so scrollbar/chrome changes are visible
|
invalidateAllWindows(ctx);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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 bpp = ctx->display.format.bitsPerPixel;
|
||||||
int32_t bytesPerPx = ctx->display.format.bytesPerPixel;
|
int32_t bytesPerPx = ctx->display.format.bytesPerPixel;
|
||||||
int32_t pitch = screenW * bytesPerPx;
|
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);
|
bool dither = (bpp == 15 || bpp == 16);
|
||||||
|
|
||||||
uint8_t *buf = (uint8_t *)malloc(pitch * screenH);
|
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)
|
// for the border area, but also ensures no garbage pixels)
|
||||||
uint32_t bgPx = ctx->colors.desktop;
|
uint32_t bgPx = ctx->colors.desktop;
|
||||||
|
|
||||||
|
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++) {
|
for (int32_t y = 0; y < screenH; y++) {
|
||||||
uint8_t *dst = buf + y * pitch;
|
uint32_t *row = (uint32_t *)(buf + y * pitch);
|
||||||
|
int32_t pairs = screenW / 2;
|
||||||
for (int32_t x = 0; x < screenW; x++) {
|
for (int32_t i = 0; i < pairs; i++) {
|
||||||
writePixel(dst, x, bgPx, bpp);
|
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) {
|
if (mode == WallpaperStretchE) {
|
||||||
// Bilinear scale to screen dimensions with optional dither
|
// Bilinear scale to screen dimensions with optional dither
|
||||||
for (int32_t y = 0; y < screenH; y++) {
|
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);
|
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 sy0 = srcYfp >> 16;
|
||||||
int32_t sy1 = (sy0 + 1 < imgH) ? sy0 + 1 : imgH - 1;
|
int32_t sy1 = (sy0 + 1 < imgH) ? sy0 + 1 : imgH - 1;
|
||||||
int32_t fy = (srcYfp >> 8) & 0xFF;
|
int32_t fy = (srcYfp >> FP_FRAC_SHIFT) & FP_FRAC_MASK;
|
||||||
int32_t ify = 256 - fy;
|
int32_t ify = FP_BLEND_MAX - fy;
|
||||||
uint8_t *dst = buf + y * pitch;
|
uint8_t *dst = buf + y * pitch;
|
||||||
const uint8_t *row0 = rgb + sy0 * srcStride;
|
const uint8_t *row0 = rgb + sy0 * srcStride;
|
||||||
const uint8_t *row1 = rgb + sy1 * srcStride;
|
const uint8_t *row1 = rgb + sy1 * srcStride;
|
||||||
|
|
||||||
for (int32_t x = 0; x < screenW; x++) {
|
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 sx0 = srcXfp >> 16;
|
||||||
int32_t sx1 = (sx0 + 1 < imgW) ? sx0 + 1 : imgW - 1;
|
int32_t sx1 = (sx0 + 1 < imgW) ? sx0 + 1 : imgW - 1;
|
||||||
int32_t fx = (srcXfp >> 8) & 0xFF;
|
int32_t fx = (srcXfp >> FP_FRAC_SHIFT) & FP_FRAC_MASK;
|
||||||
int32_t ifx = 256 - fx;
|
int32_t ifx = FP_BLEND_MAX - fx;
|
||||||
|
|
||||||
const uint8_t *p00 = row0 + sx0 * 3;
|
const uint8_t *p00 = row0 + sx0 * RGB_CHANNELS;
|
||||||
const uint8_t *p10 = row0 + sx1 * 3;
|
const uint8_t *p10 = row0 + sx1 * RGB_CHANNELS;
|
||||||
const uint8_t *p01 = row1 + sx0 * 3;
|
const uint8_t *p01 = row1 + sx0 * RGB_CHANNELS;
|
||||||
const uint8_t *p11 = row1 + sx1 * 3;
|
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 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 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;
|
int32_t b = (p00[2] * ifx * ify + p10[2] * fx * ify + p01[2] * ifx * fy + p11[2] * fx * fy) >> 16;
|
||||||
|
|
||||||
if (dither) {
|
if (dither) {
|
||||||
int32_t d = bayerMatrix[y & 3][x & 3];
|
ditherRgb(&r, &g, &b, y, x);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
} else if (mode == WallpaperTileE) {
|
||||||
// Tile: repeat the image at native size across the screen
|
// Tile: repeat the image at native size across the screen
|
||||||
for (int32_t y = 0; y < screenH; y++) {
|
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);
|
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++) {
|
for (int32_t x = 0; x < screenW; x++) {
|
||||||
int32_t srcX = x % imgW;
|
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 r = src[0];
|
||||||
int32_t g = src[1];
|
int32_t g = src[1];
|
||||||
int32_t b = src[2];
|
int32_t b = src[2];
|
||||||
|
|
||||||
if (dither) {
|
if (dither) {
|
||||||
int32_t d = bayerMatrix[y & 3][x & 3];
|
ditherRgb(&r, &g, &b, y, x);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
} 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++) {
|
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);
|
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++) {
|
for (int32_t sx = srcStartX; sx < srcEndX; sx++) {
|
||||||
int32_t dx = offX + 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 r = src[0];
|
||||||
int32_t g = src[1];
|
int32_t g = src[1];
|
||||||
int32_t b = src[2];
|
int32_t b = src[2];
|
||||||
|
|
||||||
if (dither) {
|
if (dither) {
|
||||||
int32_t d = bayerMatrix[dy & 3][dx & 3];
|
ditherRgb(&r, &g, &b, dy, dx);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// popup menu item index calculation can use multiply+shift instead
|
||||||
// of division. On a 486, integer divide is 40+ cycles; this
|
// of division. On a 486, integer divide is 40+ cycles; this
|
||||||
// reciprocal trick reduces it to ~10 cycles (imul + shr).
|
// 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
|
// Dirty the entire screen so the first compositeAndFlush paints everything
|
||||||
dirtyListAdd(&ctx->dirty, 0, 0, ctx->display.width, ctx->display.height);
|
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 y = 0; y < imgH; y++) {
|
||||||
for (int32_t x = 0; x < imgW; x++) {
|
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]);
|
uint32_t color = packColor(d, src[0], src[1], src[2]);
|
||||||
uint8_t *dst = buf + y * pitch + x * bpp;
|
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;
|
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);
|
free(rgb);
|
||||||
|
|
||||||
|
|
@ -2699,7 +2780,7 @@ int32_t dvxScreenshot(AppContextT *ctx, const char *path) {
|
||||||
return -1;
|
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);
|
free(rgb);
|
||||||
|
|
||||||
|
|
@ -2784,7 +2865,7 @@ int32_t dvxWindowScreenshot(AppContextT *ctx, WindowT *win, const char *path) {
|
||||||
return -1;
|
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);
|
free(rgb);
|
||||||
|
|
||||||
|
|
@ -2809,17 +2890,7 @@ int32_t dvxWindowScreenshot(AppContextT *ctx, WindowT *win, const char *path) {
|
||||||
void dvxTileWindows(AppContextT *ctx) {
|
void dvxTileWindows(AppContextT *ctx) {
|
||||||
int32_t screenW = ctx->display.width;
|
int32_t screenW = ctx->display.width;
|
||||||
int32_t screenH = ctx->display.height;
|
int32_t screenH = ctx->display.height;
|
||||||
|
int32_t count = countVisibleWindows(ctx);
|
||||||
// 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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2880,17 +2951,7 @@ void dvxTileWindows(AppContextT *ctx) {
|
||||||
void dvxTileWindowsH(AppContextT *ctx) {
|
void dvxTileWindowsH(AppContextT *ctx) {
|
||||||
int32_t screenW = ctx->display.width;
|
int32_t screenW = ctx->display.width;
|
||||||
int32_t screenH = ctx->display.height;
|
int32_t screenH = ctx->display.height;
|
||||||
|
int32_t count = countVisibleWindows(ctx);
|
||||||
// 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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2928,17 +2989,7 @@ void dvxTileWindowsH(AppContextT *ctx) {
|
||||||
void dvxTileWindowsV(AppContextT *ctx) {
|
void dvxTileWindowsV(AppContextT *ctx) {
|
||||||
int32_t screenW = ctx->display.width;
|
int32_t screenW = ctx->display.width;
|
||||||
int32_t screenH = ctx->display.height;
|
int32_t screenH = ctx->display.height;
|
||||||
|
int32_t count = countVisibleWindows(ctx);
|
||||||
// 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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
return;
|
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 *dragItem; // item being dragged (NULL = none)
|
||||||
struct WidgetT *dropTarget; // insertion target (NULL = none)
|
struct WidgetT *dropTarget; // insertion target (NULL = none)
|
||||||
bool dropAfter; // true = insert after target, false = before
|
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;
|
} treeView;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,14 @@
|
||||||
#include <sys/nearptr.h>
|
#include <sys/nearptr.h>
|
||||||
#include <sys/farptr.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
|
// Prototypes
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -376,25 +384,25 @@ static void getModeInfo(uint16_t mode, DisplayT *d, int32_t *score, int32_t requ
|
||||||
int32_t s = 0;
|
int32_t s = 0;
|
||||||
|
|
||||||
if (bpp == 16) {
|
if (bpp == 16) {
|
||||||
s = 100;
|
s = MODE_SCORE_16BPP;
|
||||||
} else if (bpp == 15) {
|
} else if (bpp == 15) {
|
||||||
s = 90;
|
s = MODE_SCORE_15BPP;
|
||||||
} else if (bpp == 32) {
|
} else if (bpp == 32) {
|
||||||
s = 85;
|
s = MODE_SCORE_32BPP;
|
||||||
} else if (bpp == 8) {
|
} else if (bpp == 8) {
|
||||||
s = 70;
|
s = MODE_SCORE_8BPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the user's preferred bpp
|
// Prefer the user's preferred bpp
|
||||||
if (bpp == preferredBpp) {
|
if (bpp == preferredBpp) {
|
||||||
s += 20;
|
s += MODE_SCORE_PREF_BPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exact resolution match is preferred
|
// Exact resolution match is preferred
|
||||||
if (w == requestedW && h == requestedH) {
|
if (w == requestedW && h == requestedH) {
|
||||||
s += 10;
|
s += MODE_SCORE_EXACT_RES;
|
||||||
} else {
|
} else {
|
||||||
s -= 10;
|
s -= MODE_SCORE_EXACT_RES;
|
||||||
}
|
}
|
||||||
|
|
||||||
*score = s;
|
*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
|
// 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
|
// the entire item array on every layout pass. Items are stored as
|
||||||
// external pointers (not copied) -- the caller owns the string data.
|
// external pointers (not copied) -- the caller owns the string data.
|
||||||
int32_t maxLen = 0;
|
w->as.comboBox.maxItemLen = widgetMaxItemLen(items, count);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (w->as.comboBox.selectedIdx >= count) {
|
if (w->as.comboBox.selectedIdx >= count) {
|
||||||
w->as.comboBox.selectedIdx = -1;
|
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;
|
uint32_t arrowFg = w->enabled ? colors->contentFg : colors->windowShadow;
|
||||||
int32_t arrowX = w->x + textAreaW + DROPDOWN_BTN_WIDTH / 2;
|
int32_t arrowX = w->x + textAreaW + DROPDOWN_BTN_WIDTH / 2;
|
||||||
int32_t arrowY = w->y + w->h / 2 - 1;
|
int32_t arrowY = w->y + w->h / 2 - 1;
|
||||||
|
widgetDrawDropdownArrow(d, ops, arrowX, arrowY, arrowFg);
|
||||||
for (int32_t i = 0; i < 4; i++) {
|
|
||||||
drawHLine(d, ops, arrowX - 3 + i, arrowY + i, 7 - i * 2, 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
|
// 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
|
// widgetNavigateIndex
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -59,17 +59,7 @@ void wgtDropdownSetItems(WidgetT *w, const char **items, int32_t count) {
|
||||||
w->as.dropdown.itemCount = count;
|
w->as.dropdown.itemCount = count;
|
||||||
|
|
||||||
// Cache max item strlen to avoid recomputing in calcMinSize
|
// Cache max item strlen to avoid recomputing in calcMinSize
|
||||||
int32_t maxLen = 0;
|
w->as.dropdown.maxItemLen = widgetMaxItemLen(items, count);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (w->as.dropdown.selectedIdx >= count) {
|
if (w->as.dropdown.selectedIdx >= count) {
|
||||||
w->as.dropdown.selectedIdx = -1;
|
w->as.dropdown.selectedIdx = -1;
|
||||||
|
|
@ -254,16 +244,11 @@ void widgetDropdownPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
||||||
btnBevel.width = 2;
|
btnBevel.width = 2;
|
||||||
drawBevel(d, ops, w->x + textAreaW, w->y, DROPDOWN_BTN_WIDTH, w->h, &btnBevel);
|
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
|
// Down arrow glyph in button
|
||||||
// lines of decreasing width (7, 5, 3, 1 pixels). This creates a 4-pixel
|
|
||||||
// tall downward-pointing triangle centered in the button.
|
|
||||||
uint32_t arrowFg = w->enabled ? colors->contentFg : colors->windowShadow;
|
uint32_t arrowFg = w->enabled ? colors->contentFg : colors->windowShadow;
|
||||||
int32_t arrowX = w->x + textAreaW + DROPDOWN_BTN_WIDTH / 2;
|
int32_t arrowX = w->x + textAreaW + DROPDOWN_BTN_WIDTH / 2;
|
||||||
int32_t arrowY = w->y + w->h / 2 - 1;
|
int32_t arrowY = w->y + w->h / 2 - 1;
|
||||||
|
widgetDrawDropdownArrow(d, ops, arrowX, arrowY, arrowFg);
|
||||||
for (int32_t i = 0; i < 4; i++) {
|
|
||||||
drawHLine(d, ops, arrowX - 3 + i, arrowY + i, 7 - i * 2, arrowFg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (w->focused) {
|
if (w->focused) {
|
||||||
drawFocusRect(d, ops, w->x + 3, w->y + 3, textAreaW - 6, w->h - 6, fg);
|
drawFocusRect(d, ops, w->x + 3, w->y + 3, textAreaW - 6, w->h - 6, fg);
|
||||||
|
|
|
||||||
|
|
@ -925,6 +925,8 @@ void widgetReorderDrop(WidgetT *w) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w->as.treeView.dimsValid = false;
|
||||||
|
|
||||||
// Unlink drag from its current parent
|
// Unlink drag from its current parent
|
||||||
WidgetT *oldParent = drag->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.
|
// 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);
|
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).
|
// 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);
|
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;
|
w->as.listBox.itemCount = count;
|
||||||
|
|
||||||
// Cache max item strlen to avoid recomputing in calcMinSize
|
// Cache max item strlen to avoid recomputing in calcMinSize
|
||||||
int32_t maxLen = 0;
|
w->as.listBox.maxItemLen = widgetMaxItemLen(items, count);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (w->as.listBox.selectedIdx >= count) {
|
if (w->as.listBox.selectedIdx >= count) {
|
||||||
w->as.listBox.selectedIdx = count > 0 ? 0 : -1;
|
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->cols = cols;
|
||||||
w->as.listView->colCount = count;
|
w->as.listView->colCount = count;
|
||||||
w->as.listView->totalColW = 0;
|
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);
|
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;
|
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);
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,16 @@ void wgtDestroy(WidgetT *w) {
|
||||||
return;
|
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) {
|
if (w->parent) {
|
||||||
widgetRemoveChild(w->parent, w);
|
widgetRemoveChild(w->parent, w);
|
||||||
}
|
}
|
||||||
|
|
@ -555,6 +565,17 @@ void wgtSetTooltip(WidgetT *w, const char *text) {
|
||||||
void wgtSetVisible(WidgetT *w, bool visible) {
|
void wgtSetVisible(WidgetT *w, bool visible) {
|
||||||
if (w) {
|
if (w) {
|
||||||
w->visible = visible;
|
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);
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,10 @@
|
||||||
// the actual node reparenting via widgetRemoveChild/widgetAddChild.
|
// the actual node reparenting via widgetRemoveChild/widgetAddChild.
|
||||||
//
|
//
|
||||||
// Performance note: calcTreeItemsHeight and calcTreeItemsMaxWidth
|
// Performance note: calcTreeItemsHeight and calcTreeItemsMaxWidth
|
||||||
// walk the full visible tree on every call. For trees with hundreds
|
// walk the full visible tree, but results are cached in the TreeView's
|
||||||
// of items this could be optimized with caching (as TextArea does
|
// cachedTotalHeight/cachedMaxWidth fields. The cache is invalidated
|
||||||
// for line count). In practice, DOS-era tree views are small enough
|
// (dimsValid = false) whenever the tree structure changes: item add/
|
||||||
// that the linear scan is fast on a Pentium.
|
// remove, expand/collapse, text change, or drag-reorder.
|
||||||
|
|
||||||
#include "widgetInternal.h"
|
#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 int32_t calcTreeItemsMaxWidth(WidgetT *parent, const BitmapFontT *font, int32_t depth);
|
||||||
static void clearAllSelections(WidgetT *parent);
|
static void clearAllSelections(WidgetT *parent);
|
||||||
static WidgetT *firstVisibleItem(WidgetT *treeView);
|
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 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 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);
|
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
|
// layoutTreeItems
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -489,8 +508,14 @@ static void selectRange(WidgetT *treeView, WidgetT *from, WidgetT *to) {
|
||||||
// needed, accounting for the mutual space dependency between them.
|
// 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) {
|
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);
|
if (!w->as.treeView.dimsValid) {
|
||||||
int32_t totalW = calcTreeItemsMaxWidth(w, font, 0);
|
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 innerH = w->h - TREE_BORDER * 2;
|
||||||
int32_t innerW = w->w - TREE_BORDER * 2;
|
int32_t innerW = w->w - TREE_BORDER * 2;
|
||||||
bool needVSb = (totalH > innerH);
|
bool needVSb = (totalH > innerH);
|
||||||
|
|
@ -611,6 +636,7 @@ WidgetT *wgtTreeItem(WidgetT *parent, const char *text) {
|
||||||
if (w) {
|
if (w) {
|
||||||
w->as.treeItem.text = text;
|
w->as.treeItem.text = text;
|
||||||
w->as.treeItem.expanded = false;
|
w->as.treeItem.expanded = false;
|
||||||
|
invalidateTreeDims(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
|
|
@ -632,6 +658,7 @@ const char *widgetTreeItemGetText(const WidgetT *w) {
|
||||||
|
|
||||||
void widgetTreeItemSetText(WidgetT *w, const char *text) {
|
void widgetTreeItemSetText(WidgetT *w, const char *text) {
|
||||||
w->as.treeItem.text = text;
|
w->as.treeItem.text = text;
|
||||||
|
invalidateTreeDims(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -654,6 +681,7 @@ void wgtTreeItemSetExpanded(WidgetT *w, bool expanded) {
|
||||||
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
||||||
|
|
||||||
w->as.treeItem.expanded = expanded;
|
w->as.treeItem.expanded = expanded;
|
||||||
|
invalidateTreeDims(w);
|
||||||
wgtInvalidate(w);
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -682,6 +710,7 @@ static void treeDefaultDblClick(WidgetT *w) {
|
||||||
|
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
sel->as.treeItem.expanded = !sel->as.treeItem.expanded;
|
sel->as.treeItem.expanded = !sel->as.treeItem.expanded;
|
||||||
|
invalidateTreeDims(sel);
|
||||||
|
|
||||||
if (sel->onChange) {
|
if (sel->onChange) {
|
||||||
sel->onChange(sel);
|
sel->onChange(sel);
|
||||||
|
|
@ -875,6 +904,7 @@ void widgetTreeViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
if (!sel->as.treeItem.expanded) {
|
if (!sel->as.treeItem.expanded) {
|
||||||
sel->as.treeItem.expanded = true;
|
sel->as.treeItem.expanded = true;
|
||||||
|
w->as.treeView.dimsValid = false;
|
||||||
|
|
||||||
if (sel->onChange) {
|
if (sel->onChange) {
|
||||||
sel->onChange(sel);
|
sel->onChange(sel);
|
||||||
|
|
@ -893,6 +923,7 @@ void widgetTreeViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
||||||
if (sel) {
|
if (sel) {
|
||||||
if (sel->as.treeItem.expanded) {
|
if (sel->as.treeItem.expanded) {
|
||||||
sel->as.treeItem.expanded = false;
|
sel->as.treeItem.expanded = false;
|
||||||
|
w->as.treeView.dimsValid = false;
|
||||||
|
|
||||||
if (sel->onChange) {
|
if (sel->onChange) {
|
||||||
sel->onChange(sel);
|
sel->onChange(sel);
|
||||||
|
|
@ -915,6 +946,7 @@ void widgetTreeViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
||||||
|
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
sel->as.treeItem.expanded = !sel->as.treeItem.expanded;
|
sel->as.treeItem.expanded = !sel->as.treeItem.expanded;
|
||||||
|
w->as.treeView.dimsValid = false;
|
||||||
|
|
||||||
if (sel->onChange) {
|
if (sel->onChange) {
|
||||||
sel->onChange(sel);
|
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) {
|
if (vx >= iconX && vx < iconX + TREE_EXPAND_SIZE) {
|
||||||
clickedExpandIcon = true;
|
clickedExpandIcon = true;
|
||||||
item->as.treeItem.expanded = !item->as.treeItem.expanded;
|
item->as.treeItem.expanded = !item->as.treeItem.expanded;
|
||||||
|
hit->as.treeView.dimsValid = false;
|
||||||
|
|
||||||
// Clamp scroll positions if collapsing reduced content size
|
// Clamp scroll positions if collapsing reduced content size
|
||||||
if (!item->as.treeItem.expanded) {
|
if (!item->as.treeItem.expanded) {
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,17 @@
|
||||||
|
|
||||||
#define TM_COL_COUNT 5
|
#define TM_COL_COUNT 5
|
||||||
#define TM_MAX_PATH 260
|
#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
|
// Prototypes
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
|
static ShellAppT *getRunningAppByIndex(int32_t sel);
|
||||||
static void onTmClose(WindowT *win);
|
static void onTmClose(WindowT *win);
|
||||||
static void onTmEndTask(WidgetT *w);
|
static void onTmEndTask(WidgetT *w);
|
||||||
static void onTmRun(WidgetT *w);
|
static void onTmRun(WidgetT *w);
|
||||||
|
|
@ -52,6 +58,28 @@ static const char **sCells = NULL; // dynamic array of cell pointers
|
||||||
static TmRowStringsT *sRowStrs = NULL; // dynamic array of per-row strings
|
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
|
// onTmClose
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -86,22 +114,11 @@ static void onTmEndTask(WidgetT *w) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-walk the app slot table in the same order as refreshTaskList()
|
ShellAppT *app = getRunningAppByIndex(sel);
|
||||||
// to map the selected row index back to the correct ShellAppT.
|
|
||||||
int32_t idx = 0;
|
|
||||||
|
|
||||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
if (app) {
|
||||||
ShellAppT *app = shellGetApp(i);
|
|
||||||
|
|
||||||
if (app && app->state == AppStateRunningE) {
|
|
||||||
if (idx == sel) {
|
|
||||||
shellForceKillApp(sCtx, app);
|
shellForceKillApp(sCtx, app);
|
||||||
shellDesktopUpdate();
|
shellDesktopUpdate();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,20 +159,18 @@ static void onTmSwitchTo(WidgetT *w) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same index-to-appId mapping as refreshTaskList. Scan the window
|
ShellAppT *app = getRunningAppByIndex(sel);
|
||||||
// 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;
|
|
||||||
|
|
||||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
if (!app) {
|
||||||
ShellAppT *app = shellGetApp(i);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (app && app->state == AppStateRunningE) {
|
// Scan the window stack top-down (highest Z first) to find the app's
|
||||||
if (idx == sel) {
|
// topmost window, restore it if minimized, then raise and focus it.
|
||||||
for (int32_t j = sCtx->stack.count - 1; j >= 0; j--) {
|
for (int32_t j = sCtx->stack.count - 1; j >= 0; j--) {
|
||||||
WindowT *win = sCtx->stack.windows[j];
|
WindowT *win = sCtx->stack.windows[j];
|
||||||
|
|
||||||
if (win->appId == i) {
|
if (win->appId == app->appId) {
|
||||||
if (win->minimized) {
|
if (win->minimized) {
|
||||||
wmRestoreMinimized(&sCtx->stack, &sCtx->dirty, win);
|
wmRestoreMinimized(&sCtx->stack, &sCtx->dirty, win);
|
||||||
}
|
}
|
||||||
|
|
@ -165,13 +180,6 @@ static void onTmSwitchTo(WidgetT *w) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -283,8 +291,8 @@ void shellTaskMgrOpen(AppContextT *ctx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t winW = 520;
|
int32_t winW = TM_WIN_W;
|
||||||
int32_t winH = 280;
|
int32_t winH = TM_WIN_H;
|
||||||
int32_t winX = (ctx->display.width - winW) / 2;
|
int32_t winX = (ctx->display.width - winW) / 2;
|
||||||
int32_t winY = (ctx->display.height - winH) / 3;
|
int32_t winY = (ctx->display.height - winH) / 3;
|
||||||
|
|
||||||
|
|
@ -318,26 +326,26 @@ void shellTaskMgrOpen(AppContextT *ctx) {
|
||||||
|
|
||||||
sTmListView = wgtListView(root);
|
sTmListView = wgtListView(root);
|
||||||
sTmListView->weight = 100;
|
sTmListView->weight = 100;
|
||||||
sTmListView->prefH = wgtPixels(160);
|
sTmListView->prefH = wgtPixels(TM_LIST_PREF_H);
|
||||||
wgtListViewSetColumns(sTmListView, tmCols, TM_COL_COUNT);
|
wgtListViewSetColumns(sTmListView, tmCols, TM_COL_COUNT);
|
||||||
|
|
||||||
WidgetT *btnRow = wgtHBox(root);
|
WidgetT *btnRow = wgtHBox(root);
|
||||||
btnRow->spacing = wgtPixels(8);
|
btnRow->spacing = wgtPixels(TM_BTN_SPACING);
|
||||||
|
|
||||||
sTmStatusLbl = wgtLabel(btnRow, "");
|
sTmStatusLbl = wgtLabel(btnRow, "");
|
||||||
sTmStatusLbl->weight = 100;
|
sTmStatusLbl->weight = 100;
|
||||||
|
|
||||||
WidgetT *switchBtn = wgtButton(btnRow, "Switch To");
|
WidgetT *switchBtn = wgtButton(btnRow, "Switch To");
|
||||||
switchBtn->onClick = onTmSwitchTo;
|
switchBtn->onClick = onTmSwitchTo;
|
||||||
switchBtn->prefW = wgtPixels(90);
|
switchBtn->prefW = wgtPixels(TM_BTN_W);
|
||||||
|
|
||||||
WidgetT *endBtn = wgtButton(btnRow, "End Task");
|
WidgetT *endBtn = wgtButton(btnRow, "End Task");
|
||||||
endBtn->onClick = onTmEndTask;
|
endBtn->onClick = onTmEndTask;
|
||||||
endBtn->prefW = wgtPixels(90);
|
endBtn->prefW = wgtPixels(TM_BTN_W);
|
||||||
|
|
||||||
WidgetT *runBtn = wgtButton(btnRow, "Run...");
|
WidgetT *runBtn = wgtButton(btnRow, "Run...");
|
||||||
runBtn->onClick = onTmRun;
|
runBtn->onClick = onTmRun;
|
||||||
runBtn->prefW = wgtPixels(90);
|
runBtn->prefW = wgtPixels(TM_BTN_W);
|
||||||
|
|
||||||
shellRegisterDesktopUpdate(shellTaskMgrRefresh);
|
shellRegisterDesktopUpdate(shellTaskMgrRefresh);
|
||||||
refreshTaskList();
|
refreshTaskList();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue