C4StartupMainDlg::C4StartupMainDlg() : C4StartupDlg(NULL) // create w/o title; it is drawn in custom draw proc { // ctor fFirstShown = true; // screen calculations int iButtonPadding = 2; int iButtonHeight = C4GUI_BigButtonHgt; C4GUI::ComponentAligner caMain(rcBounds, 0,0,true); C4GUI::ComponentAligner caRightPanel(caMain.GetFromLeft(rcBounds.Wdt*2/5), rcBounds.Wdt/26, 40+rcBounds.Hgt/5); C4GUI::ComponentAligner caButtons(caRightPanel.GetAll(), 0, iButtonPadding); // main menu buttons C4GUI::CallbackButton<C4StartupMainDlg> *btn; AddElement(btn = new C4GUI::CallbackButton<C4StartupMainDlg>(LoadResStr("IDS_BTN_LOCALGAME"), caButtons.GetFromTop(iButtonHeight), &C4StartupMainDlg::OnStartBtn)); btn->SetToolTip(LoadResStr("IDS_DLGTIP_STARTGAME")); btn->SetCustomGraphics(&C4Startup::Get()->Graphics.barMainButtons, &C4Startup::Get()->Graphics.barMainButtonsDown); pStartButton = btn; AddElement(btn = new C4GUI::CallbackButton<C4StartupMainDlg>(LoadResStr("IDS_BTN_NETWORKGAME"), caButtons.GetFromTop(iButtonHeight), &C4StartupMainDlg::OnNetJoinBtn)); btn->SetToolTip(LoadResStr("IDS_DLGTIP_NETWORKGAME")); btn->SetCustomGraphics(&C4Startup::Get()->Graphics.barMainButtons, &C4Startup::Get()->Graphics.barMainButtonsDown); AddElement(btn = new C4GUI::CallbackButton<C4StartupMainDlg>(LoadResStr("IDS_DLG_PLAYERSELECTION"), caButtons.GetFromTop(iButtonHeight), &C4StartupMainDlg::OnPlayerSelectionBtn)); btn->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERSELECTION")); btn->SetCustomGraphics(&C4Startup::Get()->Graphics.barMainButtons, &C4Startup::Get()->Graphics.barMainButtonsDown); AddElement(btn = new C4GUI::CallbackButton<C4StartupMainDlg>(LoadResStr("IDS_DLG_OPTIONS"), caButtons.GetFromTop(iButtonHeight), &C4StartupMainDlg::OnOptionsBtn)); btn->SetToolTip(LoadResStr("IDS_DLGTIP_OPTIONS")); btn->SetCustomGraphics(&C4Startup::Get()->Graphics.barMainButtons, &C4Startup::Get()->Graphics.barMainButtonsDown); AddElement(btn = new C4GUI::CallbackButton<C4StartupMainDlg>(LoadResStr("IDS_DLG_ABOUT"), caButtons.GetFromTop(iButtonHeight), &C4StartupMainDlg::OnAboutBtn)); btn->SetToolTip(LoadResStr("IDS_DLGTIP_ABOUT")); btn->SetCustomGraphics(&C4Startup::Get()->Graphics.barMainButtons, &C4Startup::Get()->Graphics.barMainButtonsDown); AddElement(btn = new C4GUI::CallbackButton<C4StartupMainDlg>(LoadResStr("IDS_DLG_EXIT"), caButtons.GetFromTop(iButtonHeight), &C4StartupMainDlg::OnExitBtn)); btn->SetToolTip(LoadResStr("IDS_DLGTIP_EXIT")); btn->SetCustomGraphics(&C4Startup::Get()->Graphics.barMainButtons, &C4Startup::Get()->Graphics.barMainButtonsDown); // list of selected players AddElement(pParticipantsLbl = new C4GUI::Label("test", GetClientRect().Wdt*39/40, GetClientRect().Hgt*9/10, ARight, 0xffffffff, &::GraphicsResource.TitleFont, false)); pParticipantsLbl->SetToolTip(LoadResStr("IDS_DLGTIP_SELECTEDPLAYERS")); // player selection shortcut - to be made optional UpdateParticipants(); pParticipantsLbl->SetContextHandler(new C4GUI::CBContextHandler<C4StartupMainDlg>(this, &C4StartupMainDlg::OnPlayerSelContext)); // key bindings C4CustomKey::CodeList keys; keys.push_back(C4KeyCodeEx(K_DOWN)); keys.push_back(C4KeyCodeEx(K_RIGHT)); if (Config.Controls.GamepadGuiControl) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Down))); // right will be done by Dialog already } pKeyDown = new C4KeyBinding(keys, "StartupMainCtrlNext", KEYSCOPE_Gui, new C4GUI::DlgKeyCBEx<C4StartupMainDlg, bool>(*this, false, &C4StartupMainDlg::KeyAdvanceFocus), C4CustomKey::PRIO_CtrlOverride); keys.clear(); keys.push_back(C4KeyCodeEx(K_UP)); keys.push_back(C4KeyCodeEx(K_LEFT)); if (Config.Controls.GamepadGuiControl) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Up))); // left will be done by Dialog already } pKeyUp = new C4KeyBinding(keys, "StartupMainCtrlPrev", KEYSCOPE_Gui, new C4GUI::DlgKeyCBEx<C4StartupMainDlg, bool>(*this, true, &C4StartupMainDlg::KeyAdvanceFocus), C4CustomKey::PRIO_CtrlOverride); keys.clear(); keys.push_back(C4KeyCodeEx(K_RETURN)); pKeyEnter = new C4KeyBinding(keys, "StartupMainOK", KEYSCOPE_Gui, new C4GUI::DlgKeyCB<C4StartupMainDlg>(*this, &C4StartupMainDlg::KeyEnterDown, &C4StartupMainDlg::KeyEnterUp), C4CustomKey::PRIO_CtrlOverride); }
void C4GamePadControl::FeedEvent(const SDL_Event& event, int feed) { switch (event.type) { case SDL_CONTROLLERAXISMOTION: { C4KeyCode minCode = KEY_Gamepad(KEY_CONTROLLER_Axis(event.caxis.axis, false)); C4KeyCode maxCode = KEY_Gamepad(KEY_CONTROLLER_Axis(event.caxis.axis, true)); int32_t value = abs_strength(event.caxis.value); uint8_t which = event.caxis.which; C4KeyCode keyCode = event.caxis.value >= 0 ? maxCode : minCode; auto doInput = [&](C4KeyEventType event, int32_t strength) { Game.DoKeyboardInput( C4KeyCodeEx(KEY_Gamepad(keyCode), KEYS_None, false, which), event, NULL, false, strength); }; if (feed & FEED_BUTTONS) { // Also emulate button presses. if (PressedAxis.count(keyCode) && value <= deadZone) { PressedAxis.erase(keyCode); doInput(KEYEV_Up, -1); } else if (!PressedAxis.count(keyCode) && value > deadZone) { PressedAxis.insert(keyCode); doInput(KEYEV_Down, -1); } } if (feed & FEED_MOVED) doInput(KEYEV_Moved, value); AxisEvents[keyCode] = event; break; } case SDL_CONTROLLERBUTTONDOWN: if (feed & FEED_BUTTONS) Game.DoKeyboardInput( C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_Button(event.cbutton.button)), KEYS_None, false, event.cbutton.which), KEYEV_Down); break; case SDL_CONTROLLERBUTTONUP: if (feed & FEED_BUTTONS) Game.DoKeyboardInput( C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_Button(event.cbutton.button)), KEYS_None, false, event.cbutton.which), KEYEV_Up); break; } }
Button::Button(const char *szBtnText, const C4Rect &rtBounds) : Control(rtBounds), pCustomGfx(NULL), pCustomGfxDown(NULL), fDown(false), fMouseOver(false), fEnabled(true), dwCustomFontClr(0), pCustomFont(NULL) { // key callbacks C4CustomKey::CodeList keys; keys.push_back(C4KeyCodeEx(K_SPACE)); keys.push_back(C4KeyCodeEx(K_RETURN)); if (Config.Controls.GamepadGuiControl) keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton))); pKeyButton = new C4KeyBinding(keys, "GUIButtonPress", KEYSCOPE_Gui, new ControlKeyCB<Button>(*this, &Button::KeyButtonDown, &Button::KeyButtonUp), C4CustomKey::PRIO_Ctrl); sText = ""; // set new button text SetText(szBtnText); }
CheckBox::CheckBox(const C4Rect &rtBounds, const char *szCaption, bool fChecked) : Control(rtBounds), fChecked(fChecked), fMouseOn(false), fEnabled(true), pFont(NULL) , dwEnabledClr(C4GUI_CheckboxFontClr), dwDisabledClr(C4GUI_CheckboxDisabledFontClr), cHotkey(0) { if (szCaption) { sCaption.Copy(szCaption); ExpandHotkeyMarkup(sCaption, cHotkey); } // key callbacks: Check/Uncheck on space and primary joy button C4CustomKey::CodeList Keys; Keys.push_back(C4KeyCodeEx(K_SPACE)); if (Config.Controls.GamepadGuiControl) { Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton))); } pKeyCheck = new C4KeyBinding(Keys, "GUICheckboxToggle", KEYSCOPE_Gui, new ControlKeyCB<CheckBox>(*this, &CheckBox::KeyCheck), C4CustomKey::PRIO_Ctrl); pCBHandler = NULL; }
bool C4KeyboardInput::DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent, DWORD InScope, int32_t iStrength) { // store last-key-info LastKeyExtraData.iStrength = (iStrength >= 0) ? iStrength : ((InEvent != KEYEV_Up) * 100); LastKeyExtraData.game_x = LastKeyExtraData.game_y = LastKeyExtraData.vp_x = LastKeyExtraData.vp_y = C4KeyEventData::KeyPos_None; // check all key events generated by this key: First the keycode itself, then any more generic key events like KEY_Any const int32_t iKeyRangeMax = 5; int32_t iKeyRangeCnt=0, j; C4KeyCode FallbackKeys[iKeyRangeMax]; FallbackKeys[iKeyRangeCnt++] = InKey.Key; if (Key_IsGamepadButton(InKey.Key)) { uint8_t byGamepad = Key_GetGamepad(InKey.Key); uint8_t byBtnIndex = Key_GetGamepadButtonIndex(InKey.Key); // even/odd button events: Add even button indices as odd events, because byBtnIndex is zero-based and the event naming scheme is for one-based button indices if (byBtnIndex % 2) FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyEvenButton); else FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyOddButton); // high/low button events if (byBtnIndex < 4) FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyLowButton); else FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyHighButton); // "any gamepad button"-event FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyButton); } else if (Key_IsGamepadAxis(InKey.Key)) { // xy-axis-events for all even/odd axises uint8_t byGamepad = Key_GetGamepad(InKey.Key); uint8_t byAxis = Key_GetGamepadAxisIndex(InKey.Key); bool fHigh = Key_IsGamepadAxisHigh(InKey.Key); C4KeyCode keyAxisDir; if (byAxis % 2) if (fHigh) keyAxisDir = KEY_JOY_Down; else keyAxisDir = KEY_JOY_Up; else if (fHigh) keyAxisDir = KEY_JOY_Right; else keyAxisDir = KEY_JOY_Left; FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, (uint8_t)keyAxisDir); } if (InKey.Key != KEY_Any) FallbackKeys[iKeyRangeCnt++] = KEY_Any; // now get key ranges for fallback chain std::pair<KeyCodeMap::iterator, KeyCodeMap::iterator> KeyRanges[iKeyRangeMax]; assert(iKeyRangeCnt <= iKeyRangeMax); for (int32_t i = 0; i<iKeyRangeCnt; ++i) { KeyRanges[i] = KeysByCode.equal_range(FallbackKeys[i]); } // check all assigned keys // exec from highest to lowest priority unsigned int uiLastPrio = C4CustomKey::PRIO_MoreThanMax; for (;;) { KeyCodeMap::const_iterator i; // get priority to exec unsigned int uiExecPrio = C4CustomKey::PRIO_None, uiCurr; for (j = 0; j < iKeyRangeCnt; ++j) for (i = KeyRanges[j].first; i != KeyRanges[j].second; ++i) { uiCurr = i->second->GetPriority(); if (uiCurr > uiExecPrio && uiCurr < uiLastPrio) uiExecPrio = uiCurr; } // nothing with correct priority set left? if (uiExecPrio == C4CustomKey::PRIO_None) break; // exec all of this priority for (j = 0; j < iKeyRangeCnt; ++j) for (i = KeyRanges[j].first; i != KeyRanges[j].second; ++i) { C4CustomKey *pKey = i->second; assert(pKey); // check priority if (pKey->GetPriority() == uiExecPrio) // check scope and modifier // (not on release of a key that has been down, because a key release might happen with a different modifier or in different scope than its pressing!) if ((InEvent == KEYEV_Up && pKey->IsDown()) || ((pKey->GetScope() & InScope) && pKey->IsCodeMatched(C4KeyCodeEx(FallbackKeys[j], C4KeyShiftState(InKey.dwShift))))) // exec it if (pKey->Execute(InEvent, InKey)) return true; } // nothing found in this priority: exec next uiLastPrio = uiExecPrio; } // no key matched or all returned false in Execute: Not processed return false; }
C4KeyCode C4KeyCodeEx::String2KeyCode(const StdStrBuf &sName) { // direct key code? if (sName.getLength() > 2) { unsigned int dwRVal; if (sscanf(sName.getData(), "\\x%x", &dwRVal) == 1) return dwRVal; // scan code if (*sName.getData() == '$') return GetKeyByScanCode(sName.getData()); // direct gamepad code #ifdef _WIN32 if (!strnicmp(sName.getData(), "Joy", 3)) #else if (!strncasecmp(sName.getData(), "Joy", 3)) #endif { int iGamepad; if (sscanf(sName.getData(), "Joy%d", &iGamepad) == 1) { // skip Joy[number] const char *key_str = sName.getData()+4; while (isdigit(*key_str)) ++key_str; // check for button (single, uppercase letter) (e.g. Joy1A) if (*key_str && !key_str[1]) { char cGamepadButton = toupper(*key_str); if (Inside(cGamepadButton, 'A', 'Z')) { cGamepadButton = cGamepadButton - 'A'; return KEY_Gamepad(iGamepad-1, KEY_JOY_Button(cGamepadButton)); } } else { // check for standard axis (e.g. Joy1Left) if (!stricmp(key_str, "Left")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Left); if (!stricmp(key_str, "Up")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Up); if (!stricmp(key_str, "Down")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Down); if (!stricmp(key_str, "Right")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Right); // check for specific axis (e.g. Joy1Axis1Min) int iAxis; if (sscanf(key_str, "Axis%d", &iAxis) == 1 && iAxis>0) { --iAxis; // axis is 0-based internally but written 1-based in config key_str += 5; while (isdigit(*key_str)) ++key_str; if (!stricmp(key_str, "Min")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Axis(iAxis, false)); if (!stricmp(key_str, "Max")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Axis(iAxis, true)); } } } } bool is_mouse_key; #ifdef _WIN32 is_mouse_key = !strnicmp(sName.getData(), "Mouse", 5); #else is_mouse_key = !strncasecmp(sName.getData(), "Mouse", 5); #endif if (is_mouse_key) { // skip Mouse/GameMouse const char *key_str = sName.getData()+5; int mouse_id; if (sscanf(key_str, "%d", &mouse_id) == 1) { // skip number while (isdigit(*key_str)) ++key_str; // check for known mouse events (e.g. Mouse1Move or GameMouse1Wheel) if (!stricmp(key_str, "Move")) return KEY_Mouse(mouse_id-1, KEY_MOUSE_Move); if (!stricmp(key_str, "Wheel1Up")) return KEY_Mouse(mouse_id-1, KEY_MOUSE_Wheel1Up); if (!stricmp(key_str, "Wheel1Down")) return KEY_Mouse(mouse_id-1, KEY_MOUSE_Wheel1Down); if (SEqualNoCase(key_str, "Button", 6)) // e.g. Mouse1ButtonLeft or GameMouse1ButtonRightDouble { // check for known mouse button events uint8_t mouseevent_id = 0; key_str += 6; if (SEqualNoCase(key_str, "Left",4)) { mouseevent_id=KEY_MOUSE_ButtonLeft; key_str += 4; } else if (SEqualNoCase(key_str, "Right",5)) { mouseevent_id=KEY_MOUSE_ButtonRight; key_str += 5; } else if (SEqualNoCase(key_str, "Middle",6)) { mouseevent_id=KEY_MOUSE_ButtonMiddle; key_str += 6; } else if (isdigit(*key_str)) { // indexed mouse button (e.g. Mouse1Button4 or Mouse1Button4Double) int button_index; if (sscanf(key_str, "%d", &button_index) == 1) { mouseevent_id=static_cast<uint8_t>(KEY_MOUSE_Button1+button_index-1); while (isdigit(*key_str)) ++key_str; } } if (mouseevent_id) { // valid event if finished or followed by "Double" if (!*key_str) return KEY_Mouse(mouse_id-1, mouseevent_id); if (!stricmp(key_str, "Double")) return KEY_Mouse(mouse_id-1, mouseevent_id+(KEY_MOUSE_Button1Double-KEY_MOUSE_Button1)); // invalid mouse key... } } } } } // query map const C4KeyCodeMapEntry *pCheck = KeyCodeMap; while (pCheck->szName) { if (SEqualNoCase(sName.getData(), pCheck->szName)) { return(pCheck->wCode); } ++pCheck; } #if defined(USE_SDL_MAINLOOP) SDL_Scancode s = SDL_GetScancodeFromName(sName.getData()); if (s != SDL_SCANCODE_UNKNOWN) return s; #endif return KEY_Undefined; }