[BACK]Return to Buttons.c CVS log [TXT][DIR] Up to [contributed] / brogue-ce / src / brogue

Annotation of brogue-ce/src/brogue/Buttons.c, Revision 1.1.1.1

1.1       rubenllo    1: /*
                      2:  *  Buttons.c
                      3:  *  Brogue
                      4:  *
                      5:  *  Created by Brian Walker on 11/18/11.
                      6:  *  Copyright 2012. All rights reserved.
                      7:  *
                      8:  *  This file is part of Brogue.
                      9:  *
                     10:  *  This program is free software: you can redistribute it and/or modify
                     11:  *  it under the terms of the GNU Affero General Public License as
                     12:  *  published by the Free Software Foundation, either version 3 of the
                     13:  *  License, or (at your option) any later version.
                     14:  *
                     15:  *  This program is distributed in the hope that it will be useful,
                     16:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18:  *  GNU Affero General Public License for more details.
                     19:  *
                     20:  *  You should have received a copy of the GNU Affero General Public License
                     21:  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
                     22:  */
                     23:
                     24: #include "Rogue.h"
                     25: #include "IncludeGlobals.h"
                     26: #include <math.h>
                     27: #include <time.h>
                     28:
                     29: // Draws the smooth gradient that appears on a button when you hover over or depress it.
                     30: // Returns the percentage by which the current tile should be averaged toward a hilite color.
                     31: short smoothHiliteGradient(const short currentXValue, const short maxXValue) {
                     32:     return (short) (100 * sin(3.14159265 * currentXValue / maxXValue));
                     33: }
                     34:
                     35: // Draws the button to the screen, or to a display buffer if one is given.
                     36: // Button back color fades from -50% intensity at the edges to the back color in the middle.
                     37: // Text is white, but can use color escapes.
                     38: //      Hovering highlight augments fore and back colors with buttonHoverColor by 20%.
                     39: //      Pressed darkens the middle color (or turns it the hover color if the button is black).
                     40: void drawButton(brogueButton *button, enum buttonDrawStates highlight, cellDisplayBuffer dbuf[COLS][ROWS]) {
                     41:     short i, textLoc, width, midPercent, symbolNumber, opacity, oldRNG;
                     42:     color fColor, bColor, fColorBase, bColorBase, bColorEdge, bColorMid;
                     43:     enum displayGlyph displayCharacter;
                     44:
                     45:     if (!(button->flags & B_DRAW)) {
                     46:         return;
                     47:     }
                     48:     //assureCosmeticRNG;
                     49:     oldRNG = rogue.RNG;
                     50:     rogue.RNG = RNG_COSMETIC;
                     51:
                     52:     symbolNumber = 0;
                     53:
                     54:     width = strLenWithoutEscapes(button->text);
                     55:     bColorBase = button->buttonColor;
                     56:     fColorBase = ((button->flags & B_ENABLED) ? white : gray);
                     57:
                     58:     if (highlight == BUTTON_HOVER && (button->flags & B_HOVER_ENABLED)) {
                     59:         //applyColorAugment(&fColorBase, &buttonHoverColor, 20);
                     60:         //applyColorAugment(&bColorBase, &buttonHoverColor, 20);
                     61:         applyColorAverage(&fColorBase, &buttonHoverColor, 25);
                     62:         applyColorAverage(&bColorBase, &buttonHoverColor, 25);
                     63:     }
                     64:
                     65:     bColorEdge  = bColorBase;
                     66:     bColorMid   = bColorBase;
                     67:     applyColorAverage(&bColorEdge, &black, 50);
                     68:
                     69:     if (highlight == BUTTON_PRESSED) {
                     70:         applyColorAverage(&bColorMid, &black, 75);
                     71:         if (COLOR_DIFF(bColorMid, bColorBase) < 50) {
                     72:             bColorMid   = bColorBase;
                     73:             applyColorAverage(&bColorMid, &buttonHoverColor, 50);
                     74:         }
                     75:     }
                     76:     bColor = bColorMid;
                     77:
                     78:     opacity = button->opacity;
                     79:     if (highlight == BUTTON_HOVER || highlight == BUTTON_PRESSED) {
                     80:         opacity = 100 - ((100 - opacity) * opacity / 100); // Apply the opacity twice.
                     81:     }
                     82:
                     83:     for (i = textLoc = 0; i < width && i + button->x < COLS; i++, textLoc++) {
                     84:         while (button->text[textLoc] == COLOR_ESCAPE) {
                     85:             textLoc = decodeMessageColor(button->text, textLoc, &fColorBase);
                     86:         }
                     87:
                     88:         fColor = fColorBase;
                     89:
                     90:         if (button->flags & B_GRADIENT) {
                     91:             midPercent = smoothHiliteGradient(i, width - 1);
                     92:             bColor = bColorEdge;
                     93:             applyColorAverage(&bColor, &bColorMid, midPercent);
                     94:         }
                     95:
                     96:         if (highlight == BUTTON_PRESSED) {
                     97:             applyColorAverage(&fColor, &bColor, 30);
                     98:         }
                     99:
                    100:         if (button->opacity < 100) {
                    101:             applyColorAverage(&fColor, &bColor, 100 - opacity);
                    102:         }
                    103:
                    104:         bakeColor(&fColor);
                    105:         bakeColor(&bColor);
                    106:         separateColors(&fColor, &bColor);
                    107:
                    108:         displayCharacter = button->text[textLoc];
                    109:         if (button->text[textLoc] == '*') {
                    110:             if (button->symbol[symbolNumber]) {
                    111:                 displayCharacter = button->symbol[symbolNumber];
                    112:             }
                    113:             symbolNumber++;
                    114:         }
                    115:
                    116:         if (coordinatesAreInWindow(button->x + i, button->y)) {
                    117:             if (dbuf) {
                    118:                 plotCharToBuffer(displayCharacter, button->x + i, button->y, &fColor, &bColor, dbuf);
                    119:                 dbuf[button->x + i][button->y].opacity = opacity;
                    120:             } else {
                    121:                 plotCharWithColor(displayCharacter, button->x + i, button->y, &fColor, &bColor);
                    122:             }
                    123:         }
                    124:     }
                    125:     restoreRNG;
                    126: }
                    127:
                    128: void initializeButton(brogueButton *button) {
                    129:     memset((void *) button, 0, sizeof( brogueButton ));
                    130:     button->text[0] = '\0';
                    131:     button->flags |= (B_ENABLED | B_GRADIENT | B_HOVER_ENABLED | B_DRAW | B_KEYPRESS_HIGHLIGHT);
                    132:     button->buttonColor = interfaceButtonColor;
                    133:     button->opacity = 100;
                    134: }
                    135:
                    136: void drawButtonsInState(buttonState *state) {
                    137:     short i;
                    138:
                    139:     // Draw the buttons to the dbuf:
                    140:     for (i=0; i < state->buttonCount; i++) {
                    141:         if (state->buttons[i].flags & B_DRAW) {
                    142:             drawButton(&(state->buttons[i]), BUTTON_NORMAL, state->dbuf);
                    143:         }
                    144:     }
                    145: }
                    146:
                    147: void initializeButtonState(buttonState *state,
                    148:                            brogueButton *buttons,
                    149:                            short buttonCount,
                    150:                            short winX,
                    151:                            short winY,
                    152:                            short winWidth,
                    153:                            short winHeight) {
                    154:     short i, j;
                    155:
                    156:     // Initialize variables for the state struct:
                    157:     state->buttonChosen = state->buttonFocused = state->buttonDepressed = -1;
                    158:     state->buttonCount  = buttonCount;
                    159:     state->winX         = winX;
                    160:     state->winY         = winY;
                    161:     state->winWidth     = winWidth;
                    162:     state->winHeight    = winHeight;
                    163:     for (i=0; i < state->buttonCount; i++) {
                    164:         state->buttons[i] = buttons[i];
                    165:     }
                    166:     copyDisplayBuffer(state->rbuf, displayBuffer);
                    167:     clearDisplayBuffer(state->dbuf);
                    168:
                    169:     drawButtonsInState(state);
                    170:
                    171:     // Clear the rbuf so that it resets only those parts of the screen in which buttons are drawn in the first place:
                    172:     for (i=0; i<COLS; i++) {
                    173:         for (j=0; j<ROWS; j++) {
                    174:             state->rbuf[i][j].opacity = (state->dbuf[i][j].opacity ? 100 : 0);
                    175:         }
                    176:     }
                    177: }
                    178:
                    179: // Processes one round of user input, and bakes the necessary graphical changes into state->dbuf.
                    180: // Does NOT display the buttons or revert the display afterward.
                    181: // Assumes that the display has already been updated (via overlayDisplayBuffer(state->dbuf, NULL))
                    182: // and that input has been solicited (via nextBrogueEvent(event, ___, ___, ___)).
                    183: // Also relies on the buttonState having been initialized with initializeButtonState() or otherwise.
                    184: // Returns the index of a button if one is chosen.
                    185: // Otherwise, returns -1. That can be if the user canceled (in which case *canceled is true),
                    186: // or, more commonly, if the user's input in this particular split-second round was not decisive.
                    187: short processButtonInput(buttonState *state, boolean *canceled, rogueEvent *event) {
                    188:     short i, k, x, y;
                    189:     boolean buttonUsed = false;
                    190:
                    191:     // Mouse event:
                    192:     if (event->eventType == MOUSE_DOWN
                    193:         || event->eventType == MOUSE_UP
                    194:         || event->eventType == MOUSE_ENTERED_CELL) {
                    195:
                    196:         x = event->param1;
                    197:         y = event->param2;
                    198:
                    199:         // Revert the button with old focus, if any.
                    200:         if (state->buttonFocused >= 0) {
                    201:             drawButton(&(state->buttons[state->buttonFocused]), BUTTON_NORMAL, state->dbuf);
                    202:             state->buttonFocused = -1;
                    203:         }
                    204:
                    205:         // Find the button with new focus, if any.
                    206:         for (i=0; i < state->buttonCount; i++) {
                    207:             if ((state->buttons[i].flags & B_DRAW)
                    208:                 && (state->buttons[i].flags & B_ENABLED)
                    209:                 && (state->buttons[i].y == y || ((state->buttons[i].flags & B_WIDE_CLICK_AREA) && abs(state->buttons[i].y - y) <= 1))
                    210:                 && x >= state->buttons[i].x
                    211:                 && x < state->buttons[i].x + strLenWithoutEscapes(state->buttons[i].text)) {
                    212:
                    213:                 state->buttonFocused = i;
                    214:                 if (event->eventType == MOUSE_DOWN) {
                    215:                     state->buttonDepressed = i; // Keeps track of which button is down at the moment. Cleared on mouseup.
                    216:                 }
                    217:                 break;
                    218:             }
                    219:         }
                    220:         if (i == state->buttonCount) { // No focus this round.
                    221:             state->buttonFocused = -1;
                    222:         }
                    223:
                    224:         if (state->buttonDepressed >= 0) {
                    225:             if (state->buttonDepressed == state->buttonFocused) {
                    226:                 drawButton(&(state->buttons[state->buttonDepressed]), BUTTON_PRESSED, state->dbuf);
                    227:             }
                    228:         } else if (state->buttonFocused >= 0) {
                    229:             // If no button is depressed, then update the appearance of the button with the new focus, if any.
                    230:             drawButton(&(state->buttons[state->buttonFocused]), BUTTON_HOVER, state->dbuf);
                    231:         }
                    232:
                    233:         // Mouseup:
                    234:         if (event->eventType == MOUSE_UP) {
                    235:             if (state->buttonDepressed == state->buttonFocused && state->buttonFocused >= 0) {
                    236:                 // If a button is depressed, and the mouseup happened on that button, it has been chosen and we're done.
                    237:                 buttonUsed = true;
                    238:             } else {
                    239:                 // Otherwise, no button is depressed. If one was previously depressed, redraw it.
                    240:                 if (state->buttonDepressed >= 0) {
                    241:                     drawButton(&(state->buttons[state->buttonDepressed]), BUTTON_NORMAL, state->dbuf);
                    242:                 } else if (!(x >= state->winX && x < state->winX + state->winWidth
                    243:                              && y >= state->winY && y < state->winY + state->winHeight)) {
                    244:                     // Clicking outside of a button means canceling.
                    245:                     if (canceled) {
                    246:                         *canceled = true;
                    247:                     }
                    248:                 }
                    249:
                    250:                 if (state->buttonFocused >= 0) {
                    251:                     // Buttons don't hover-highlight when one is depressed, so we have to fix that when the mouse is up.
                    252:                     drawButton(&(state->buttons[state->buttonFocused]), BUTTON_HOVER, state->dbuf);
                    253:                 }
                    254:                 state->buttonDepressed = -1;
                    255:             }
                    256:         }
                    257:     }
                    258:
                    259:     // Keystroke:
                    260:     if (event->eventType == KEYSTROKE) {
                    261:
                    262:         // Cycle through all of the hotkeys of all of the buttons.
                    263:         for (i=0; i < state->buttonCount; i++) {
                    264:             for (k = 0; k < 10 && state->buttons[i].hotkey[k]; k++) {
                    265:                 if (event->param1 == state->buttons[i].hotkey[k]) {
                    266:                     // This button was chosen.
                    267:
                    268:                     if (state->buttons[i].flags & B_DRAW) {
                    269:                         // Restore the depressed and focused buttons.
                    270:                         if (state->buttonDepressed >= 0) {
                    271:                             drawButton(&(state->buttons[state->buttonDepressed]), BUTTON_NORMAL, state->dbuf);
                    272:                         }
                    273:                         if (state->buttonFocused >= 0) {
                    274:                             drawButton(&(state->buttons[state->buttonFocused]), BUTTON_NORMAL, state->dbuf);
                    275:                         }
                    276:
                    277:                         // If the button likes to flash when keypressed:
                    278:                         if (state->buttons[i].flags & B_KEYPRESS_HIGHLIGHT) {
                    279:                             // Depress the chosen button.
                    280:                             drawButton(&(state->buttons[i]), BUTTON_PRESSED, state->dbuf);
                    281:
                    282:                             // Update the display.
                    283:                             overlayDisplayBuffer(state->rbuf, NULL);
                    284:                             overlayDisplayBuffer(state->dbuf, NULL);
                    285:
                    286:                             // Wait for a little; then we're done.
                    287:                             pauseBrogue(50);
                    288:                         }
                    289:                     }
                    290:
                    291:                     state->buttonDepressed = i;
                    292:                     buttonUsed = true;
                    293:                     break;
                    294:                 }
                    295:             }
                    296:         }
                    297:
                    298:         if (!buttonUsed
                    299:             && (event->param1 == ESCAPE_KEY || event->param1 == ACKNOWLEDGE_KEY)) {
                    300:             // If the player pressed escape, we're done.
                    301:             if (canceled) {
                    302:                 *canceled = true;
                    303:             }
                    304:         }
                    305:     }
                    306:
                    307:     if (buttonUsed) {
                    308:         state->buttonChosen = state->buttonDepressed;
                    309:         return state->buttonChosen;
                    310:     } else {
                    311:         return -1;
                    312:     }
                    313: }
                    314:
                    315: // Displays a bunch of buttons and collects user input.
                    316: // Returns the index number of the chosen button, or -1 if the user cancels.
                    317: // A window region is described by winX, winY, winWidth and winHeight.
                    318: // Clicking outside of that region will constitute canceling.
                    319: short buttonInputLoop(brogueButton *buttons,
                    320:                       short buttonCount,
                    321:                       short winX,
                    322:                       short winY,
                    323:                       short winWidth,
                    324:                       short winHeight,
                    325:                       rogueEvent *returnEvent) {
                    326:     short button;
                    327:     boolean canceled;
                    328:     rogueEvent theEvent;
                    329:     buttonState state = {0};
                    330:
                    331:     assureCosmeticRNG;
                    332:
                    333:     canceled = false;
                    334:     initializeButtonState(&state, buttons, buttonCount, winX, winY, winWidth, winHeight);
                    335:
                    336:     do {
                    337:         // Update the display.
                    338:         overlayDisplayBuffer(state.dbuf, NULL);
                    339:
                    340:         // Get input.
                    341:         nextBrogueEvent(&theEvent, true, false, false);
                    342:
                    343:         // Process the input.
                    344:         button = processButtonInput(&state, &canceled, &theEvent);
                    345:
                    346:         // Revert the display.
                    347:         overlayDisplayBuffer(state.rbuf, NULL);
                    348:
                    349:     } while (button == -1 && !canceled);
                    350:
                    351:     if (returnEvent) {
                    352:         *returnEvent = theEvent;
                    353:     }
                    354:
                    355:     //overlayDisplayBuffer(dbuf, NULL); // hangs around
                    356:
                    357:     restoreRNG;
                    358:
                    359:     return button;
                    360: }

CVSweb