[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     ! 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