void drawMenuFlames(signed short flames[COLS][(ROWS + MENU_FLAME_ROW_PADDING)][3], unsigned char mask[COLS][ROWS]) { short i, j, versionStringLength; color tempColor = {0}; const color *maskColor = &black; char dchar; versionStringLength = strLenWithoutEscapes(BROGUE_VERSION_STRING); for (j=0; j<ROWS; j++) { for (i=0; i<COLS; i++) { if (j == ROWS - 1 && i >= COLS - versionStringLength) { dchar = BROGUE_VERSION_STRING[i - (COLS - versionStringLength)]; } else { dchar = ' '; } if (mask[i][j] == 100) { plotCharWithColor(dchar, i, j, &darkGray, maskColor); } else { tempColor = black; tempColor.red = flames[i][j][0] / MENU_FLAME_PRECISION_FACTOR; tempColor.green = flames[i][j][1] / MENU_FLAME_PRECISION_FACTOR; tempColor.blue = flames[i][j][2] / MENU_FLAME_PRECISION_FACTOR; if (mask[i][j] > 0) { applyColorAverage(&tempColor, maskColor, mask[i][j]); } plotCharWithColor(dchar, i, j, &darkGray, &tempColor); } } } }
boolean dialogChooseFile(char *path, const char *suffix, const char *prompt) { short i, j, count, x, y, width, height, suffixLength, pathLength, maxPathLength, currentPageStart; brogueButton buttons[FILES_ON_PAGE_MAX + 2]; fileEntry *files; boolean retval = false, again; cellDisplayBuffer dbuf[COLS][ROWS], rbuf[COLS][ROWS]; color *dialogColor = &interfaceBoxColor; char *membuf; suffixLength = strlen(suffix); files = listFiles(&count, &membuf); copyDisplayBuffer(rbuf, displayBuffer); maxPathLength = strLenWithoutEscapes(prompt); // First, we want to filter the list by stripping out any filenames that do not end with suffix. // i is the entry we're testing, and j is the entry that we move it to if it qualifies. for (i=0, j=0; i<count; i++) { pathLength = strlen(files[i].path); //printf("\nString 1: %s", &(files[i].path[(max(0, pathLength - suffixLength))])); if (stringsExactlyMatch(&(files[i].path[(max(0, pathLength - suffixLength))]), suffix)) { // This file counts! if (i > j) { files[j] = files[i]; //printf("\nMatching file: %s\twith date: %s", files[j].path, files[j].date); } j++; // Keep track of the longest length. if (min(pathLength, MAX_FILENAME_DISPLAY_LENGTH) + 10 > maxPathLength) { maxPathLength = min(pathLength, MAX_FILENAME_DISPLAY_LENGTH) + 10; } } } count = j; currentPageStart = 0; do { // Repeat to permit scrolling. again = false; for (i=0; i<min(count - currentPageStart, FILES_ON_PAGE_MAX); i++) { initializeButton(&(buttons[i])); buttons[i].flags &= ~(B_WIDE_CLICK_AREA | B_GRADIENT); buttons[i].buttonColor = *dialogColor; if (KEYBOARD_LABELS) { sprintf(buttons[i].text, "%c) ", 'a' + i); } else { buttons[i].text[0] = '\0'; } strncat(buttons[i].text, files[currentPageStart+i].path, MAX_FILENAME_DISPLAY_LENGTH); // Clip off the file suffix from the button text. buttons[i].text[strlen(buttons[i].text) - suffixLength] = '\0'; // Snip! buttons[i].hotkey[0] = 'a' + i; buttons[i].hotkey[1] = 'A' + i; // Clip the filename length if necessary. if (strlen(buttons[i].text) > MAX_FILENAME_DISPLAY_LENGTH) { strcpy(&(buttons[i].text[MAX_FILENAME_DISPLAY_LENGTH - 3]), "..."); } //printf("\nFound file: %s, with date: %s", files[currentPageStart+i].path, files[currentPageStart+i].date); } x = (COLS - maxPathLength) / 2; width = maxPathLength; height = min(count - currentPageStart, FILES_ON_PAGE_MAX) + 2; y = max(4, (ROWS - height) / 2); for (i=0; i<min(count - currentPageStart, FILES_ON_PAGE_MAX); i++) { pathLength = strlen(buttons[i].text); for (j=pathLength; j<(width - 8); j++) { buttons[i].text[j] = ' '; } buttons[i].text[j] = '\0'; strcpy(&(buttons[i].text[j]), files[currentPageStart+i].date); buttons[i].x = x; buttons[i].y = y + 1 + i; } if (count > FILES_ON_PAGE_MAX) { // Create up and down arrows. initializeButton(&(buttons[i])); strcpy(buttons[i].text, " * "); buttons[i].symbol[0] = UP_ARROW_CHAR; if (currentPageStart <= 0) { buttons[i].flags &= ~(B_ENABLED | B_DRAW); } else { buttons[i].hotkey[0] = UP_ARROW; buttons[i].hotkey[1] = NUMPAD_8; buttons[i].hotkey[2] = PAGE_UP_KEY; } buttons[i].x = x + (width - 11)/2; buttons[i].y = y; i++; initializeButton(&(buttons[i])); strcpy(buttons[i].text, " * "); buttons[i].symbol[0] = DOWN_ARROW_CHAR; if (currentPageStart + FILES_ON_PAGE_MAX >= count) { buttons[i].flags &= ~(B_ENABLED | B_DRAW); } else { buttons[i].hotkey[0] = DOWN_ARROW; buttons[i].hotkey[1] = NUMPAD_2; buttons[i].hotkey[2] = PAGE_DOWN_KEY; } buttons[i].x = x + (width - 11)/2; buttons[i].y = y + i; } if (count) { clearDisplayBuffer(dbuf); printString(prompt, x, y - 1, &itemMessageColor, dialogColor, dbuf); rectangularShading(x - 1, y - 1, width + 1, height + 1, dialogColor, INTERFACE_OPACITY, dbuf); overlayDisplayBuffer(dbuf, NULL); // for (j=0; j<min(count - currentPageStart, FILES_ON_PAGE_MAX); j++) { // printf("\nSanity check BEFORE: %s, with date: %s", files[currentPageStart+j].path, files[currentPageStart+j].date); // printf("\n (button name)Sanity check BEFORE: %s", buttons[j].text); // } i = buttonInputLoop(buttons, min(count - currentPageStart, FILES_ON_PAGE_MAX) + (count > FILES_ON_PAGE_MAX ? 2 : 0), x, y, width, height, NULL); // for (j=0; j<min(count - currentPageStart, FILES_ON_PAGE_MAX); j++) { // printf("\nSanity check AFTER: %s, with date: %s", files[currentPageStart+j].path, files[currentPageStart+j].date); // printf("\n (button name)Sanity check AFTER: %s", buttons[j].text); // } overlayDisplayBuffer(rbuf, NULL); if (i < min(count - currentPageStart, FILES_ON_PAGE_MAX)) { if (i >= 0) { retval = true; strcpy(path, files[currentPageStart+i].path); } else { // i is -1 retval = false; } } else if (i == min(count - currentPageStart, FILES_ON_PAGE_MAX)) { // Up arrow again = true; currentPageStart -= FILES_ON_PAGE_MAX; } else if (i == min(count - currentPageStart, FILES_ON_PAGE_MAX) + 1) { // Down arrow again = true; currentPageStart += FILES_ON_PAGE_MAX; } } } while (again); free(files); free(membuf); if (count == 0) { dialogAlert("No applicable files found."); return false; } else { return retval; } }
void drawMenuFlames( BROGUE_DRAW_CONTEXT *context, signed short flames[COLS][(ROWS + MENU_FLAME_ROW_PADDING)][3]) { short i, j, versionStringLength; char dchar; versionStringLength = strLenWithoutEscapes(BROGUE_VERSION_STRING); for (j=0; j<ROWS; j++) { for (i=0; i<COLS; i++) { if (j == ROWS - 1 && i >= COLS - versionStringLength) { dchar = BROGUE_VERSION_STRING[i - (COLS - versionStringLength)]; } else { dchar = ' '; } BrogueDrawContext_setForeground( context, colorForDisplay(darkGray)); if (i < COLS - 1) { color ul, ur, bl, br; memset(&ul, 0, sizeof(color)); memset(&ur, 0, sizeof(color)); memset(&bl, 0, sizeof(color)); memset(&br, 0, sizeof(color)); ul.red = flames[i][j][0] / MENU_FLAME_PRECISION_FACTOR; ul.green = flames[i][j][1] / MENU_FLAME_PRECISION_FACTOR; ul.blue = flames[i][j][2] / MENU_FLAME_PRECISION_FACTOR; ur.red = flames[i + 1][j][0] / MENU_FLAME_PRECISION_FACTOR; ur.green = flames[i + 1][j][1] / MENU_FLAME_PRECISION_FACTOR; ur.blue = flames[i + 1][j][2] / MENU_FLAME_PRECISION_FACTOR; bl.red = flames[i][j + 1][0] / MENU_FLAME_PRECISION_FACTOR; bl.green = flames[i][j + 1][1] / MENU_FLAME_PRECISION_FACTOR; bl.blue = flames[i][j + 1][2] / MENU_FLAME_PRECISION_FACTOR; br.red = flames[i + 1][j + 1][0] / MENU_FLAME_PRECISION_FACTOR; br.green = flames[i + 1][j + 1][1] / MENU_FLAME_PRECISION_FACTOR; br.blue = flames[i + 1][j + 1][2] / MENU_FLAME_PRECISION_FACTOR; BrogueDrawContext_blendBackground( context, colorForDisplay(ul), colorForDisplay(ur), colorForDisplay(bl), colorForDisplay(br)); } else { color upper, lower; upper.red = flames[i][j][0] / MENU_FLAME_PRECISION_FACTOR; upper.green = flames[i][j][1] / MENU_FLAME_PRECISION_FACTOR; upper.blue = flames[i][j][2] / MENU_FLAME_PRECISION_FACTOR; lower.red = flames[i][j + 1][0] / MENU_FLAME_PRECISION_FACTOR; lower.green = flames[i][j + 1][1] / MENU_FLAME_PRECISION_FACTOR; lower.blue = flames[i][j + 1][2] / MENU_FLAME_PRECISION_FACTOR; BrogueDrawContext_blendBackground( context, colorForDisplay(upper), colorForDisplay(upper), colorForDisplay(lower), colorForDisplay(lower)); } BrogueDrawContext_drawChar(context, i, j, dchar); } } }
boolean dialogChooseFile(char *path, const char *suffix, const char *prompt) { short i, j, count, x, y, width, height, suffixLength, pathLength, maxPathLength, currentPageStart; brogueButton buttons[FILES_ON_PAGE_MAX + 2]; fileEntry *files; boolean retval = false, again; color *dialogColor = &interfaceBoxColor; char *membuf; suffixLength = strlen(suffix); files = listFiles(&count, &membuf); // copyDisplayBuffer(rbuf, displayBuffer); maxPathLength = strLenWithoutEscapes(prompt); // First, we want to filter the list by stripping out any filenames that do not end with suffix. // i is the entry we're testing, and j is the entry that we move it to if it qualifies. for (i=0, j=0; i<count; i++) { pathLength = strlen(files[i].path); //printf("\nString 1: %s", &(files[i].path[(max(0, pathLength - suffixLength))])); if (stringsExactlyMatch(&(files[i].path[(max(0, pathLength - suffixLength))]), suffix)) { // This file counts! if (i > j) { files[j] = files[i]; //printf("\nMatching file: %s\twith date: %s", files[j].path, files[j].date); } j++; // Keep track of the longest length. if (min(pathLength, MAX_FILENAME_DISPLAY_LENGTH) + 10 > maxPathLength) { maxPathLength = min(pathLength, MAX_FILENAME_DISPLAY_LENGTH) + 10; } } } count = j; currentPageStart = 0; do { // Repeat to permit scrolling. again = false; for (i=0; i<min(count - currentPageStart, FILES_ON_PAGE_MAX); i++) { initializeButton(&(buttons[i])); buttons[i].flags &= ~(B_WIDE_CLICK_AREA | B_GRADIENT); buttons[i].buttonColor = *dialogColor; sprintf(buttons[i].text, "%c)\t", 'a' + i); strncat(buttons[i].text, files[currentPageStart+i].path, MAX_FILENAME_DISPLAY_LENGTH); // Clip off the file suffix from the button text. buttons[i].text[strlen(buttons[i].text) - suffixLength] = '\0'; // Snip! buttons[i].hotkey[0] = 'a' + i; buttons[i].hotkey[1] = 'A' + i; // Clip the filename length if necessary. if (strlen(buttons[i].text) > MAX_FILENAME_DISPLAY_LENGTH) { strcpy(&(buttons[i].text[MAX_FILENAME_DISPLAY_LENGTH - 3]), "..."); } //printf("\nFound file: %s, with date: %s", files[currentPageStart+i].path, files[currentPageStart+i].date); } x = (COLS - maxPathLength) / 2; width = maxPathLength; height = min(count - currentPageStart, FILES_ON_PAGE_MAX) + 5; y = max(4, (ROWS - height) / 2); for (i=0; i<min(count - currentPageStart, FILES_ON_PAGE_MAX); i++) { strcat(buttons[i].text, "\t"); strcat(buttons[i].text, files[currentPageStart+i].date); buttons[i].x = 1; buttons[i].y = 3 + i; buttons[i].width = width; } if (count > FILES_ON_PAGE_MAX) { // Create up and down arrows. initializeButton(&(buttons[i])); strcpy(buttons[i].text, " * "); buttons[i].symbol[0] = UP_ARROW_CHAR; if (currentPageStart <= 0) { buttons[i].flags &= ~(B_ENABLED | B_DRAW); } else { buttons[i].hotkey[0] = UP_ARROW; buttons[i].hotkey[1] = NUMPAD_8; buttons[i].hotkey[2] = PAGE_UP_KEY; } buttons[i].x = (width - 11)/2; buttons[i].y = 2; i++; initializeButton(&(buttons[i])); strcpy(buttons[i].text, " * "); buttons[i].symbol[0] = DOWN_ARROW_CHAR; if (currentPageStart + FILES_ON_PAGE_MAX >= count) { buttons[i].flags &= ~(B_ENABLED | B_DRAW); } else { buttons[i].hotkey[0] = DOWN_ARROW; buttons[i].hotkey[1] = NUMPAD_2; buttons[i].hotkey[2] = PAGE_DOWN_KEY; } buttons[i].x = (width - 11)/2; buttons[i].y = 2 + i; } if (count) { BROGUE_WINDOW *root, *window; BROGUE_DRAW_CONTEXT *context; BROGUE_EFFECT *effect; int tabStops[2] = { 3, width - 8 }; root = ioGetRoot(); window = BrogueWindow_open( root, (COLS - width) / 2, (ROWS - height) / 2, width, height); context = BrogueDrawContext_open(window); effect = BrogueEffect_open(context, BUTTON_EFFECT_NAME); BrogueWindow_setColor(window, windowColor); BrogueDrawContext_enableProportionalFont(context, 1); BrogueDrawContext_setTabStops(context, 2, tabStops); BrogueDrawContext_drawAsciiString(context, 1, 1, prompt); // for (j=0; j<min(count - currentPageStart, FILES_ON_PAGE_MAX); j++) { // printf("\nSanity check BEFORE: %s, with date: %s", files[currentPageStart+j].path, files[currentPageStart+j].date); // printf("\n (button name)Sanity check BEFORE: %s", buttons[j].text); // } i = buttonInputLoop(buttons, min(count - currentPageStart, FILES_ON_PAGE_MAX) + (count > FILES_ON_PAGE_MAX ? 2 : 0), context, effect, x, y, width, height, NULL); BrogueWindow_close(window); // for (j=0; j<min(count - currentPageStart, FILES_ON_PAGE_MAX); j++) { // printf("\nSanity check AFTER: %s, with date: %s", files[currentPageStart+j].path, files[currentPageStart+j].date); // printf("\n (button name)Sanity check AFTER: %s", buttons[j].text); // } if (i < min(count - currentPageStart, FILES_ON_PAGE_MAX)) { if (i >= 0) { retval = true; strcpy(path, files[currentPageStart+i].path); } else { // i is -1 retval = false; } } else if (i == min(count - currentPageStart, FILES_ON_PAGE_MAX)) { // Up arrow again = true; currentPageStart -= FILES_ON_PAGE_MAX; } else if (i == min(count - currentPageStart, FILES_ON_PAGE_MAX) + 1) { // Down arrow again = true; currentPageStart += FILES_ON_PAGE_MAX; } } } while (again); free(files); free(membuf); if (count == 0) { dialogAlert("找不到合适的文件。"); return false; } else { return retval; } }
// Draws the button to the screen, or to a display buffer if one is given. // Button back color fades from -50% intensity at the edges to the back color in the middle. // Text is white, but can use color escapes. // Hovering highlight augments fore and back colors with buttonHoverColor by 20%. // Pressed darkens the middle color (or turns it the hover color if the button is black). void drawButton(brogueButton *button, enum buttonDrawStates highlight, cellDisplayBuffer dbuf[COLS][ROWS]) { short i, textLoc, width, midPercent, symbolNumber, opacity, oldRNG; color fColor, bColor, fColorBase, bColorBase, bColorEdge, bColorMid; uchar displayCharacter; if (!(button->flags & B_DRAW)) { return; } //assureCosmeticRNG; oldRNG = rogue.RNG; rogue.RNG = RNG_COSMETIC; symbolNumber = 0; width = strLenWithoutEscapes(button->text); bColorBase = button->buttonColor; fColorBase = ((button->flags & B_ENABLED) ? white : gray); if (highlight == BUTTON_HOVER && (button->flags & B_HOVER_ENABLED)) { //applyColorAugment(&fColorBase, &buttonHoverColor, 20); //applyColorAugment(&bColorBase, &buttonHoverColor, 20); applyColorAverage(&fColorBase, &buttonHoverColor, 25); applyColorAverage(&bColorBase, &buttonHoverColor, 25); } bColorEdge = bColorBase; bColorMid = bColorBase; applyColorAverage(&bColorEdge, &black, 50); if (highlight == BUTTON_PRESSED) { applyColorAverage(&bColorMid, &black, 75); if (COLOR_DIFF(bColorMid, bColorBase) < 50) { bColorMid = bColorBase; applyColorAverage(&bColorMid, &buttonHoverColor, 50); } } bColor = bColorMid; opacity = button->opacity; if (highlight == BUTTON_HOVER || highlight == BUTTON_PRESSED) { opacity = 100 - ((100 - opacity) * opacity / 100); // Apply the opacity twice. } for (i = textLoc = 0; i < width && i + button->x < COLS; i++, textLoc++) { while (button->text[textLoc] == COLOR_ESCAPE) { textLoc = decodeMessageColor(button->text, textLoc, &fColorBase); } fColor = fColorBase; if (button->flags & B_GRADIENT) { midPercent = smoothHiliteGradient(i, width - 1); bColor = bColorEdge; applyColorAverage(&bColor, &bColorMid, midPercent); } if (highlight == BUTTON_PRESSED) { applyColorAverage(&fColor, &bColor, 30); } if (button->opacity < 100) { applyColorAverage(&fColor, &bColor, 100 - opacity); } bakeColor(&fColor); bakeColor(&bColor); separateColors(&fColor, &bColor); displayCharacter = button->text[textLoc]; if (button->text[textLoc] == '*') { if (button->symbol[symbolNumber]) { displayCharacter = button->symbol[symbolNumber]; } symbolNumber++; } if (coordinatesAreInWindow(button->x + i, button->y)) { if (dbuf) { plotCharToBuffer(displayCharacter, button->x + i, button->y, &fColor, &bColor, dbuf); dbuf[button->x + i][button->y].opacity = opacity; } else { plotCharWithColor(displayCharacter, button->x + i, button->y, &fColor, &bColor); } } } restoreRNG; }
// Processes one round of user input, and bakes the necessary graphical changes into state->dbuf. // Does NOT display the buttons or revert the display afterward. // Assumes that the display has already been updated (via overlayDisplayBuffer(state->dbuf, NULL)) // and that input has been solicited (via nextBrogueEvent(event, ___, ___, ___)). // Also relies on the buttonState having been initialized with initializeButtonState() or otherwise. // Returns the index of a button if one is chosen. // Otherwise, returns -1. That can be if the user canceled (in which case *canceled is true), // or, more commonly, if the user's input in this particular split-second round was not decisive. short processButtonInput(buttonState *state, boolean *canceled, rogueEvent *event) { short i, k, x, y; boolean buttonUsed = false; // Mouse event: if (event->eventType == MOUSE_DOWN || event->eventType == MOUSE_UP || event->eventType == MOUSE_ENTERED_CELL) { x = event->param1; y = event->param2; // Revert the button with old focus, if any. if (state->buttonFocused >= 0) { drawButton(&(state->buttons[state->buttonFocused]), BUTTON_NORMAL, state->dbuf); state->buttonFocused = -1; } // Find the button with new focus, if any. for (i=0; i < state->buttonCount; i++) { if ((state->buttons[i].flags & B_DRAW) && (state->buttons[i].flags & B_ENABLED) && (state->buttons[i].y == y || ((state->buttons[i].flags & B_WIDE_CLICK_AREA) && abs(state->buttons[i].y - y) <= 1)) && x >= state->buttons[i].x && x < state->buttons[i].x + strLenWithoutEscapes(state->buttons[i].text)) { state->buttonFocused = i; if (event->eventType == MOUSE_DOWN) { state->buttonDepressed = i; // Keeps track of which button is down at the moment. Cleared on mouseup. } break; } } if (i == state->buttonCount) { // No focus this round. state->buttonFocused = -1; } if (state->buttonDepressed >= 0) { if (state->buttonDepressed == state->buttonFocused) { drawButton(&(state->buttons[state->buttonDepressed]), BUTTON_PRESSED, state->dbuf); } } else if (state->buttonFocused >= 0) { // If no button is depressed, then update the appearance of the button with the new focus, if any. drawButton(&(state->buttons[state->buttonFocused]), BUTTON_HOVER, state->dbuf); } // Mouseup: if (event->eventType == MOUSE_UP) { if (state->buttonDepressed == state->buttonFocused && state->buttonFocused >= 0) { // If a button is depressed, and the mouseup happened on that button, it has been chosen and we're done. buttonUsed = true; } else { // Otherwise, no button is depressed. If one was previously depressed, redraw it. if (state->buttonDepressed >= 0) { drawButton(&(state->buttons[state->buttonDepressed]), BUTTON_NORMAL, state->dbuf); } else if (!(x >= state->winX && x < state->winX + state->winWidth && y >= state->winY && y < state->winY + state->winHeight)) { // Clicking outside of a button means canceling. if (canceled) { *canceled = true; } } if (state->buttonFocused >= 0) { // Buttons don't hover-highlight when one is depressed, so we have to fix that when the mouse is up. drawButton(&(state->buttons[state->buttonFocused]), BUTTON_HOVER, state->dbuf); } state->buttonDepressed = -1; } } } // Keystroke: if (event->eventType == KEYSTROKE) { // Cycle through all of the hotkeys of all of the buttons. for (i=0; i < state->buttonCount; i++) { for (k = 0; k < 10 && state->buttons[i].hotkey[k]; k++) { if (event->param1 == state->buttons[i].hotkey[k]) { // This button was chosen. if (state->buttons[i].flags & B_DRAW) { // Restore the depressed and focused buttons. if (state->buttonDepressed >= 0) { drawButton(&(state->buttons[state->buttonDepressed]), BUTTON_NORMAL, state->dbuf); } if (state->buttonFocused >= 0) { drawButton(&(state->buttons[state->buttonFocused]), BUTTON_NORMAL, state->dbuf); } // If the button likes to flash when keypressed: if (state->buttons[i].flags & B_KEYPRESS_HIGHLIGHT) { // Depress the chosen button. drawButton(&(state->buttons[i]), BUTTON_PRESSED, state->dbuf); // Update the display. overlayDisplayBuffer(state->rbuf, NULL); overlayDisplayBuffer(state->dbuf, NULL); // Wait for a little; then we're done. pauseBrogue(50); } } state->buttonDepressed = i; buttonUsed = true; break; } } } if (!buttonUsed && (event->param1 == ESCAPE_KEY || event->param1 == ACKNOWLEDGE_KEY)) { // If the player pressed escape, we're done. if (canceled) { *canceled = true; } } } if (buttonUsed) { state->buttonChosen = state->buttonDepressed; return state->buttonChosen; } else { return -1; } }