/* ** settingsmenu.cpp ** ** This file is part of mkxp. ** ** Copyright (C) 2014 Jonas Kulla ** ** mkxp is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 2 of the License, or ** (at your option) any later version. ** ** mkxp is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with mkxp. If not, see . */ #include "settingsmenu.h" #include #include #include #include #include #include "keybindings.h" #include "eventthread.h" #include "font.h" #include "input.h" #include "etc-internal.h" #include "util.h" #include "sharedstate.h" #include "i18n.h" #include #include const Vec2i winSize(700, 500); const uint8_t cBgNorm = 50; const uint8_t cBgDark = 20; const uint8_t cLine = 0; const uint8_t cText = 255; static bool pointInRect(const SDL_Rect &r, int x, int y){ return (x >= r.x && x <= r.x+r.w && y >= r.y && y <= r.y+r.h); } typedef SettingsMenuPrivate SMP; #define BTN_STRING(btn, id) { Input:: btn, id, #btn } struct VButton{ Input::ButtonCode code; int trstrId; const char *str; } static const vButtons[] = { BTN_STRING(Up, TRSTR_KEYBIND_UP), BTN_STRING(Left, TRSTR_KEYBIND_LEFT), BTN_STRING(Action, TRSTR_KEYBIND_ACTION), BTN_STRING(Cancel, TRSTR_KEYBIND_CANCEL), BTN_STRING(Menu, TRSTR_KEYBIND_MENU), BTN_STRING(L, TRSTR_KEYBIND_L), BTN_STRING(Down, TRSTR_KEYBIND_DOWN), BTN_STRING(Right, TRSTR_KEYBIND_RIGHT), BTN_STRING(Run, TRSTR_KEYBIND_RUN), BTN_STRING(Deactivate, TRSTR_KEYBIND_DEACTIVATE), BTN_STRING(Items, TRSTR_KEYBIND_ITEMS), BTN_STRING(R, TRSTR_KEYBIND_R), }; static elementsN(vButtons); const char* getButtonName(int buttonId) { switch (buttonId) { case 0: return findtext(TRSTR_KEYBIND_A, "A Button"); break; case 1: return findtext(TRSTR_KEYBIND_B, "B Button"); break; case 2: return findtext(TRSTR_KEYBIND_X, "X Button"); break; case 3: return findtext(TRSTR_KEYBIND_Y, "Y Button"); break; case 4: return findtext(TRSTR_KEYBIND_BACK, "Back Button"); break; case 5: return findtext(TRSTR_KEYBIND_GUIDE, "Guide Button"); break; case 6: return findtext(TRSTR_KEYBIND_START, "Start Button"); break; case 7: return findtext(TRSTR_KEYBIND_LSTICK, "Left Stick"); break; case 8: return findtext(TRSTR_KEYBIND_RSTICK, "Right Stick"); break; case 9: return findtext(TRSTR_KEYBIND_LSHOULDER, "Left Shoulder"); break; case 10: return findtext(TRSTR_KEYBIND_RSHOULDER, "Right Shoulder"); break; case 11: return findtext(TRSTR_KEYBIND_PADU, "D-Pad (Up)"); break; case 12: return findtext(TRSTR_KEYBIND_PADD, "D-Pad (Down)"); break; case 13: return findtext(TRSTR_KEYBIND_PADL, "D-Pad (Left)"); break; case 14: return findtext(TRSTR_KEYBIND_PADR, "D-Pad (Right)"); break; default: return findtext(TRSTR_KEYBIND_UNK, "Unknown key"); } } /* Human readable string representation */ std::string sourceDescString(const SourceDesc &src){ char buf[128]; char pos; switch (src.type){ case Invalid: return std::string(); case Key:{ if (src.d.scan == SDL_SCANCODE_LSHIFT) return findtext(TRSTR_KEYBIND_SHIFT, "Shift"); SDL_Keycode key = SDL_GetKeyFromScancode(src.d.scan, SDL_KMOD_NONE, false); const char *str = SDL_GetKeyName(key); if (*str == '\0') { return findtext(TRSTR_KEYBIND_UNK, "Unknown key"); } else { snprintf(buf, sizeof(buf), findtext(TRSTR_KEYBIND_KEYFMT, "%s Key"), str); return buf; } } case CButton: snprintf(buf, sizeof(buf), "%s", getButtonName(src.d.jb)); return buf; case CAxis: switch (src.d.ja.axis) { case SDL_GAMEPAD_AXIS_LEFTX: if (src.d.ja.dir == Negative) return findtext(TRSTR_KEYBIND_LSTICKL, "Left Stick (Left)"); else return findtext(TRSTR_KEYBIND_LSTICKR, "Left Stick (Right)"); case SDL_GAMEPAD_AXIS_LEFTY: if (src.d.ja.dir == Negative) return findtext(TRSTR_KEYBIND_LSTICKU, "Left Stick (Up)"); else return findtext(TRSTR_KEYBIND_LSTICKD, "Left Stick (Down)"); case SDL_GAMEPAD_AXIS_RIGHTX: if (src.d.ja.dir == Negative) return findtext(TRSTR_KEYBIND_RSTICKL, "Right Stick (Left)"); else return findtext(TRSTR_KEYBIND_RSTICKR, "Right Stick (Right)"); case SDL_GAMEPAD_AXIS_RIGHTY: if (src.d.ja.dir == Negative) return findtext(TRSTR_KEYBIND_RSTICKU, "Right Stick (Up)"); else return findtext(TRSTR_KEYBIND_RSTICKD, "Right Stick (Down)"); case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: return findtext(TRSTR_KEYBIND_LTRIGGER, "Left Trigger"); case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: return findtext(TRSTR_KEYBIND_RTRIGGER, "Right Trigger"); } return ""; case JButton: snprintf(buf, sizeof(buf), findtext(TRSTR_KEYBIND_JOYBTTNFMT, "Joy Button %d"), src.d.jb); return buf; case JHat: switch(src.d.jh.pos){ case SDL_HAT_UP: pos = 'U'; break; case SDL_HAT_DOWN: pos = 'D'; break; case SDL_HAT_LEFT: pos = 'L'; break; case SDL_HAT_RIGHT: pos = 'R'; break; default: pos = '-'; } snprintf(buf, sizeof(buf), findtext(TRSTR_KEYBIND_JOYHATFMT, "Joy Hat %d:%c"), src.d.jh.hat, pos); return buf; case JAxis: snprintf(buf, sizeof(buf), findtext(TRSTR_KEYBIND_JOYAXISFMT, "Joy Axis %d%c"), src.d.ja.axis, src.d.ja.dir == Negative ? '-' : '+'); return buf; } assert(!"unreachable"); return ""; } struct Widget{ /* Widgets have a static size and position, * defined at creation */ Widget(SMP *p, const IntRect &rect); /* Public methods take coordinates in global * window coordinates */ bool hit(int x, int y); void draw(SDL_Surface *surf); void motion(int x, int y); void leave(); void click(int x, int y, uint8_t button); protected: SMP *p; IntRect rect; /* Protected abstract methods are called with * widget-local coordinates */ virtual void drawHandler(SDL_Surface *surf) = 0; virtual void motionHandler(int x, int y) = 0; virtual void leaveHandler() = 0; virtual void clickHandler(int x, int y, uint8_t button) = 0; }; struct BindingWidget : Widget{ VButton vb; /* Source slots */ SourceDesc src[4]; /* Flag indicating whether a slot source is used * for multiple button targets (red indicator) */ bool dupFlag[4]; BindingWidget(int vbIndex, SMP *p, const IntRect &rect) : Widget(p, rect), vb(vButtons[vbIndex]), hoveredCell(-1) {} void appendBindings(BDescVec &d) const; protected: int hoveredCell; void setHoveredCell(int cell); /* Get the slot cell index that contains (x,y), * or -1 if none */ int cellIndex(int x, int y) const; void drawHandler(SDL_Surface *surf); void motionHandler(int x, int y); void leaveHandler(); void clickHandler(int x, int y, uint8_t button); }; struct Button : Widget{ typedef void (SMP::*Callback)(); const char *str; Callback cb; Button(SMP *p, const IntRect &rect, const char *str, Callback cb) : Widget(p, rect), str(str), cb(cb), hovered(false) {} protected: bool hovered; void setHovered(bool val); void drawHandler(SDL_Surface *surf); void motionHandler(int, int); void leaveHandler(); void clickHandler(int, int, uint8_t button); }; struct Label : Widget{ const char *str; SDL_Color c; Label() : Widget(0, IntRect()) {} Label(SMP *p, const IntRect &rect, const char *str, uint8_t r, uint8_t g, uint8_t b) : Widget(p, rect), str(str), visible(true) { c.r = r; c.g = g; c.b = b; c.a = 255; } void setVisible(bool val); protected: bool visible; void drawHandler(SDL_Surface *surf); void motionHandler(int, int) {} void leaveHandler() {} void clickHandler(int, int, uint8_t) {} }; enum State{ Idle, AwaitingInput }; enum Justification{ Left, Center }; struct SettingsMenuPrivate{ State state; /* Necessary to decide which window gets to * process joystick events */ bool hasFocus; /* Tell the outer EventThread to destroy us */ bool destroyReq; /* Offset added for all draw calls */ Vec2i drawOff; SDL_Window *window; SDL_Surface *winSurf; uint32_t winID; TTF_Font *font; SDL_PixelFormat *rgb; RGSSThreadData &rtData; std::vector bWidgets; std::vector