mirror of
https://github.com/The-Powder-Toy/The-Powder-Toy.git
synced 2025-03-15 11:49:52 +01:00
Implement keyboard hotkey bindings (heavily squashed)
- add keyboard bindings button to options - hooked keyboard binding panel to button - WIP bindings drawn in activity - keyboard binding keys finally appear - keyboard binding input widget - reserved keys logic working - removed debug logs - pref saved on key release - more reserved keys - fixed memory bad alloc error - modifier bindings - scroll panel for bindings view - added sdl id to bindings - cache sdl scan id into prefs - function ids are getting picked up - more keyboard mappings - more key bindings + reserved keys - keyboard bindings working - write keyboard bindings pref on load - sync new functions to prefs - final touches - Keep those include trees flat (see 0179cefc) - Fix tabs - WIP - many to one binding - WriteDefaultPrefs is now instance method - model add, remove and save methods - clear prefs before saving - added method for checking binding conflict - notify view on key combo change - route binding notification to gameview - view foundations for overhaul - fixed memory issue + has conflicting key issue - fixed prefs not being cleared before save - override text input to do nothing - fixed many complications due to duplicated hotkeys - missing index on new model caused problems - WIP - view adaptation - WIP - add and remove button layout - more patches - fixed empty textboxes problem - WIP - frontend overhaul - fixed ordering issue - binding removal - wip - function store to hold no shortcut data - reset to defaults button added - add from no shortcut works - error message on conflict - better summary for PopBindingByFunctionId - keyboard bindings hooked to gameview keypress - do not return correcty function id if no shortcut - flatten include trees - remove debug comment - spaces to tabs
This commit is contained in:
parent
51b78be139
commit
bf0cc9ba5f
@ -551,7 +551,7 @@ if GetOption('no-install-prompt'):
|
||||
|
||||
|
||||
#Generate list of sources to compile
|
||||
sources = Glob("src/*.cpp") + Glob("src/*/*.cpp") + Glob("src/*/*/*.cpp") + Glob("generated/*.cpp") + Glob("data/*.cpp")
|
||||
sources = Glob("src/*.cpp") + Glob("src/*/*.cpp") + Glob("src/*/*/*.cpp") + Glob("src/*/*/*/*.cpp") + Glob("generated/*.cpp") + Glob("data/*.cpp")
|
||||
if not GetOption('nolua') and not GetOption('renderer') and not GetOption('font'):
|
||||
sources += Glob("src/lua/socket/*.c") + Glob("src/lua/LuaCompat.c")
|
||||
|
||||
|
@ -1946,6 +1946,11 @@ std::vector<bool> Client::GetPrefBoolArray(ByteString prop)
|
||||
return std::vector<bool>();
|
||||
}
|
||||
|
||||
Json::Value Client::GetPrefJson(ByteString prop, Json::Value defaultValue)
|
||||
{
|
||||
return GetPref(preferences, prop, defaultValue);
|
||||
}
|
||||
|
||||
// Helper preference setting function.
|
||||
// To actually save any changes to preferences, we need to directly do preferences[property] = thing
|
||||
// any other way will set the value of a copy of preferences, not the original
|
||||
@ -1979,6 +1984,18 @@ void Client::SetPref(ByteString prop, Json::Value value)
|
||||
}
|
||||
}
|
||||
|
||||
void Client::ClearPref(ByteString prop)
|
||||
{
|
||||
try
|
||||
{
|
||||
preferences[prop].clear();
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetPref(ByteString prop, std::vector<Json::Value> value)
|
||||
{
|
||||
try
|
||||
|
@ -181,10 +181,12 @@ public:
|
||||
std::vector<int> GetPrefIntegerArray(ByteString prop);
|
||||
std::vector<unsigned int> GetPrefUIntegerArray(ByteString prop);
|
||||
std::vector<bool> GetPrefBoolArray(ByteString prop);
|
||||
Json::Value GetPrefJson(ByteString prop, Json::Value defaultValue = Json::nullValue);
|
||||
|
||||
void SetPref(ByteString prop, Json::Value value);
|
||||
void SetPref(ByteString property, std::vector<Json::Value> value);
|
||||
void SetPrefUnicode(ByteString prop, String value);
|
||||
void ClearPref(ByteString prop);
|
||||
};
|
||||
|
||||
#endif // CLIENT_H
|
||||
|
@ -691,6 +691,14 @@ void GameModel::SetSave(SaveInfo * newSave, bool invertIncludePressure)
|
||||
UpdateQuickOptions();
|
||||
}
|
||||
|
||||
void GameModel::NotifyKeyBindingsChanged()
|
||||
{
|
||||
for (auto observer : observers)
|
||||
{
|
||||
observer->NotifyKeyBindingsChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
SaveFile * GameModel::GetSaveFile()
|
||||
{
|
||||
return currentFile;
|
||||
|
@ -208,6 +208,7 @@ public:
|
||||
void SetMouseClickRequired(bool mouseClickRequired);
|
||||
bool GetIncludePressure();
|
||||
void SetIncludePressure(bool includePressure);
|
||||
void NotifyKeyBindingsChanged();
|
||||
|
||||
std::vector<Notification*> GetNotifications();
|
||||
void AddNotification(Notification * notification);
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "simulation/SimulationData.h"
|
||||
#include "simulation/ElementDefs.h"
|
||||
#include "ElementClasses.h"
|
||||
#include "gui/options/keyboardbindings/KeyboardBindingsMap.h"
|
||||
|
||||
#ifdef GetUserName
|
||||
# undef GetUserName // dammit windows
|
||||
@ -462,6 +463,10 @@ GameView::GameView():
|
||||
};
|
||||
colourPicker = new ui::Button(ui::Point((XRES/2)-8, YRES+1), ui::Point(16, 16), "", "Pick Colour");
|
||||
colourPicker->SetActionCallback(new ColourPickerAction(this));
|
||||
|
||||
// write keyboard bindings prefs if they are absent
|
||||
keyboardBindingModel.LoadBindingPrefs();
|
||||
keyboardBindingModel.WriteDefaultPrefs();
|
||||
}
|
||||
|
||||
GameView::~GameView()
|
||||
@ -589,6 +594,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void GameView::NotifyKeyBindingsChanged(GameModel * sender)
|
||||
{
|
||||
// resync the model
|
||||
keyboardBindingModel.LoadBindingPrefs();
|
||||
}
|
||||
|
||||
void GameView::NotifyQuickOptionsChanged(GameModel * sender)
|
||||
{
|
||||
for (size_t i = 0; i < quickOptionButtons.size(); i++)
|
||||
@ -1440,55 +1451,58 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl,
|
||||
}
|
||||
}
|
||||
|
||||
int32_t functionId = keyboardBindingModel.GetFunctionForBinding(scan, shift, ctrl, alt);
|
||||
|
||||
if (repeat)
|
||||
return;
|
||||
bool didKeyShortcut = true;
|
||||
switch(scan)
|
||||
|
||||
// please see KeyboardBindingsMap.h for mappings
|
||||
switch(functionId)
|
||||
{
|
||||
case KeyboardBindingFunction::TOGGLE_CONSOLE:
|
||||
{
|
||||
case SDL_SCANCODE_GRAVE:
|
||||
SDL_StopTextInput();
|
||||
SDL_StartTextInput();
|
||||
c->ShowConsole();
|
||||
break;
|
||||
case SDL_SCANCODE_SPACE: //Space
|
||||
}
|
||||
case KeyboardBindingFunction::PAUSE_SIMULATION: //Space
|
||||
c->SetPaused();
|
||||
break;
|
||||
case SDL_SCANCODE_Z:
|
||||
case KeyboardBindingFunction::UNDO:
|
||||
if (selectMode != SelectNone && isMouseDown)
|
||||
break;
|
||||
if (ctrl && !isMouseDown)
|
||||
if (!isMouseDown)
|
||||
{
|
||||
if (shift)
|
||||
c->HistoryForward();
|
||||
else
|
||||
c->HistoryRestore();
|
||||
}
|
||||
else
|
||||
{
|
||||
isMouseDown = false;
|
||||
zoomCursorFixed = false;
|
||||
c->SetZoomEnabled(true);
|
||||
c->HistoryRestore();
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_P:
|
||||
case SDL_SCANCODE_F2:
|
||||
if (ctrl)
|
||||
case KeyboardBindingFunction::REDO:
|
||||
if (selectMode != SelectNone && isMouseDown)
|
||||
break;
|
||||
if (!isMouseDown)
|
||||
{
|
||||
if (shift)
|
||||
c->SetActiveTool(1, "DEFAULT_UI_PROPERTY");
|
||||
else
|
||||
c->SetActiveTool(0, "DEFAULT_UI_PROPERTY");
|
||||
c->HistoryForward();
|
||||
}
|
||||
else
|
||||
screenshot();
|
||||
break;
|
||||
case SDL_SCANCODE_F3:
|
||||
case KeyboardBindingFunction::ENABLE_ZOOM:
|
||||
{
|
||||
isMouseDown = false;
|
||||
zoomCursorFixed = false;
|
||||
c->SetZoomEnabled(true);
|
||||
break;
|
||||
}
|
||||
case KeyboardBindingFunction::PROPERTY_TOOL:
|
||||
c->SetActiveTool(1, "DEFAULT_UI_PROPERTY");
|
||||
break;
|
||||
case KeyboardBindingFunction::TOGGLE_DEBUG_HUD:
|
||||
SetDebugHUD(!GetDebugHUD());
|
||||
break;
|
||||
case SDL_SCANCODE_F5:
|
||||
case KeyboardBindingFunction::RELOAD_SIMULATION:
|
||||
c->ReloadSim();
|
||||
break;
|
||||
case SDL_SCANCODE_A:
|
||||
case KeyboardBindingFunction::SAVE_AUTHORSHIP_INFO:
|
||||
if ((Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator
|
||||
|| Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin) && ctrl)
|
||||
{
|
||||
@ -1496,120 +1510,96 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl,
|
||||
new InformationMessage("Save authorship info", authorString.FromUtf8(), true);
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_R:
|
||||
if (ctrl)
|
||||
c->ReloadSim();
|
||||
break;
|
||||
case SDL_SCANCODE_E:
|
||||
case KeyboardBindingFunction::OPEN_ELEMENT_SEARCH:
|
||||
c->OpenElementSearch();
|
||||
break;
|
||||
case SDL_SCANCODE_F:
|
||||
if (ctrl)
|
||||
{
|
||||
Tool *active = c->GetActiveTool(0);
|
||||
if (!active->GetIdentifier().Contains("_PT_") || (ren->findingElement == active->GetToolID()))
|
||||
ren->findingElement = 0;
|
||||
else
|
||||
ren->findingElement = active->GetToolID();
|
||||
}
|
||||
case KeyboardBindingFunction::FIND_MODE:
|
||||
{
|
||||
Tool *active = c->GetActiveTool(0);
|
||||
if (!active->GetIdentifier().Contains("_PT_") || (ren->findingElement == active->GetToolID()))
|
||||
ren->findingElement = 0;
|
||||
else
|
||||
c->FrameStep();
|
||||
ren->findingElement = active->GetToolID();
|
||||
break;
|
||||
case SDL_SCANCODE_G:
|
||||
if (ctrl)
|
||||
c->ShowGravityGrid();
|
||||
else if(shift)
|
||||
c->AdjustGridSize(-1);
|
||||
else
|
||||
c->AdjustGridSize(1);
|
||||
}
|
||||
case KeyboardBindingFunction::FRAME_STEP:
|
||||
c->FrameStep();
|
||||
break;
|
||||
case SDL_SCANCODE_F1:
|
||||
case KeyboardBindingFunction::SHOW_GRAVITY_GRID:
|
||||
c->ShowGravityGrid();
|
||||
break;
|
||||
case KeyboardBindingFunction::DECREASE_GRAVITY_GRID_SIZE:
|
||||
c->AdjustGridSize(-1);
|
||||
break;
|
||||
case KeyboardBindingFunction::INCREASE_GRAVITY_GRID_SIZE:
|
||||
c->AdjustGridSize(1);
|
||||
break;
|
||||
case KeyboardBindingFunction::TOGGLE_INTRO_TEXT:
|
||||
if(!introText)
|
||||
introText = 8047;
|
||||
else
|
||||
introText = 0;
|
||||
break;
|
||||
case SDL_SCANCODE_H:
|
||||
if(ctrl)
|
||||
{
|
||||
if(!introText)
|
||||
introText = 8047;
|
||||
else
|
||||
introText = 0;
|
||||
}
|
||||
else
|
||||
showHud = !showHud;
|
||||
case KeyboardBindingFunction::TOGGLE_HUD:
|
||||
showHud = !showHud;
|
||||
break;
|
||||
case SDL_SCANCODE_B:
|
||||
if(ctrl)
|
||||
c->SetDecoration();
|
||||
else
|
||||
if (colourPicker->GetParentWindow())
|
||||
c->SetActiveMenu(lastMenu);
|
||||
else
|
||||
{
|
||||
c->SetDecoration(true);
|
||||
c->SetPaused(true);
|
||||
c->SetActiveMenu(SC_DECO);
|
||||
}
|
||||
case KeyboardBindingFunction::TOGGLE_DECORATIONS_LAYER:
|
||||
c->SetDecoration();
|
||||
break;
|
||||
case SDL_SCANCODE_Y:
|
||||
if (ctrl)
|
||||
{
|
||||
c->HistoryForward();
|
||||
}
|
||||
case KeyboardBindingFunction::TOGGLE_DECORATION_TOOL:
|
||||
if (colourPicker->GetParentWindow())
|
||||
c->SetActiveMenu(lastMenu);
|
||||
else
|
||||
{
|
||||
c->SwitchAir();
|
||||
c->SetDecoration(true);
|
||||
c->SetPaused(true);
|
||||
c->SetActiveMenu(SC_DECO);
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
case SDL_SCANCODE_Q:
|
||||
case KeyboardBindingFunction::TOGGLE_AIR_MODE:
|
||||
c->SwitchAir();
|
||||
break;
|
||||
case KeyboardBindingFunction::QUIT:
|
||||
ui::Engine::Ref().ConfirmExit();
|
||||
break;
|
||||
case SDL_SCANCODE_U:
|
||||
case KeyboardBindingFunction::TOGGLE_HEAT:
|
||||
c->ToggleAHeat();
|
||||
break;
|
||||
case SDL_SCANCODE_N:
|
||||
case KeyboardBindingFunction::TOGGLE_NEWTONIAN_GRAVITY:
|
||||
c->ToggleNewtonianGravity();
|
||||
break;
|
||||
case SDL_SCANCODE_EQUALS:
|
||||
if(ctrl)
|
||||
c->ResetSpark();
|
||||
else
|
||||
c->ResetAir();
|
||||
case KeyboardBindingFunction::RESET_SPARK:
|
||||
c->ResetSpark();
|
||||
break;
|
||||
case SDL_SCANCODE_C:
|
||||
if(ctrl)
|
||||
case KeyboardBindingFunction::RESET_AIR:
|
||||
c->ResetAir();
|
||||
break;
|
||||
case KeyboardBindingFunction::COPY:
|
||||
{
|
||||
selectMode = SelectCopy;
|
||||
selectPoint1 = selectPoint2 = ui::Point(-1, -1);
|
||||
isMouseDown = false;
|
||||
buttonTip = "\x0F\xEF\xEF\020Click-and-drag to specify an area to copy (right click = cancel)";
|
||||
buttonTipShow = 120;
|
||||
break;
|
||||
}
|
||||
case KeyboardBindingFunction::CUT:
|
||||
{
|
||||
selectMode = SelectCut;
|
||||
selectPoint1 = selectPoint2 = ui::Point(-1, -1);
|
||||
isMouseDown = false;
|
||||
buttonTip = "\x0F\xEF\xEF\020Click-and-drag to specify an area to copy then cut (right click = cancel)";
|
||||
buttonTipShow = 120;
|
||||
break;
|
||||
}
|
||||
case KeyboardBindingFunction::PASTE:
|
||||
if (c->LoadClipboard())
|
||||
{
|
||||
selectMode = SelectCopy;
|
||||
selectPoint1 = selectPoint2 = ui::Point(-1, -1);
|
||||
selectPoint1 = selectPoint2 = mousePosition;
|
||||
isMouseDown = false;
|
||||
buttonTip = "\x0F\xEF\xEF\020Click-and-drag to specify an area to copy (right click = cancel)";
|
||||
buttonTipShow = 120;
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_X:
|
||||
if(ctrl)
|
||||
{
|
||||
selectMode = SelectCut;
|
||||
selectPoint1 = selectPoint2 = ui::Point(-1, -1);
|
||||
isMouseDown = false;
|
||||
buttonTip = "\x0F\xEF\xEF\020Click-and-drag to specify an area to copy then cut (right click = cancel)";
|
||||
buttonTipShow = 120;
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_V:
|
||||
if (ctrl)
|
||||
{
|
||||
if (c->LoadClipboard())
|
||||
{
|
||||
selectPoint1 = selectPoint2 = mousePosition;
|
||||
isMouseDown = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_L:
|
||||
case KeyboardBindingFunction::STAMP_TOOL:
|
||||
{
|
||||
std::vector<ByteString> stampList = Client::Ref().GetStamps(0, 1);
|
||||
if (stampList.size())
|
||||
@ -1624,34 +1614,36 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl,
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SDL_SCANCODE_K:
|
||||
case KeyboardBindingFunction::OPEN_STAMPS:
|
||||
{
|
||||
selectMode = SelectNone;
|
||||
selectPoint1 = selectPoint2 = ui::Point(-1, -1);
|
||||
c->OpenStamps();
|
||||
break;
|
||||
case SDL_SCANCODE_RIGHTBRACKET:
|
||||
}
|
||||
case KeyboardBindingFunction::INCREASE_BRUSH_SIZE:
|
||||
if(zoomEnabled && !zoomCursorFixed)
|
||||
c->AdjustZoomSize(1, !alt);
|
||||
else
|
||||
c->AdjustBrushSize(1, !alt, shiftBehaviour, ctrlBehaviour);
|
||||
break;
|
||||
case SDL_SCANCODE_LEFTBRACKET:
|
||||
case KeyboardBindingFunction::DECREASE_BRUSH_SIZE:
|
||||
if(zoomEnabled && !zoomCursorFixed)
|
||||
c->AdjustZoomSize(-1, !alt);
|
||||
else
|
||||
c->AdjustBrushSize(-1, !alt, shiftBehaviour, ctrlBehaviour);
|
||||
break;
|
||||
case SDL_SCANCODE_I:
|
||||
if(ctrl)
|
||||
c->Install();
|
||||
else
|
||||
c->InvertAirSim();
|
||||
case KeyboardBindingFunction::INSTALL_GAME:
|
||||
c->Install();
|
||||
break;
|
||||
case SDL_SCANCODE_SEMICOLON:
|
||||
if (ctrl)
|
||||
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^SPECIFIC_DELETE);
|
||||
else
|
||||
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^REPLACE_MODE);
|
||||
case KeyboardBindingFunction::INVERT_AIR_SIMULATION:
|
||||
c->InvertAirSim();
|
||||
break;
|
||||
case KeyboardBindingFunction::TOGGLE_REPLACE_MODE:
|
||||
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^SPECIFIC_DELETE);
|
||||
break;
|
||||
case KeyboardBindingFunction::TOGGLE_SPECIFIC_DELETE_MODE:
|
||||
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^REPLACE_MODE);
|
||||
break;
|
||||
default:
|
||||
didKeyShortcut = false;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "common/String.h"
|
||||
#include "gui/interface/Window.h"
|
||||
#include "simulation/Sample.h"
|
||||
#include "gui/options/keyboardbindings/KeyboardBindingsModel.h"
|
||||
|
||||
enum DrawMode
|
||||
{
|
||||
@ -114,6 +115,8 @@ private:
|
||||
|
||||
SimulationSample sample;
|
||||
|
||||
KeyboardBindingsModel keyboardBindingModel;
|
||||
|
||||
void updateToolButtonScroll();
|
||||
|
||||
void SetSaveButtonTooltips();
|
||||
@ -182,6 +185,7 @@ public:
|
||||
void NotifyInfoTipChanged(GameModel * sender);
|
||||
void NotifyQuickOptionsChanged(GameModel * sender);
|
||||
void NotifyLastToolChanged(GameModel * sender);
|
||||
void NotifyKeyBindingsChanged(GameModel * sender);
|
||||
|
||||
|
||||
void ToolTip(ui::Point senderPosition, String toolTip) override;
|
||||
|
@ -24,6 +24,7 @@ namespace ui
|
||||
|
||||
int GetScrollLimit();
|
||||
void SetScrollPosition(int position);
|
||||
inline float GetScrollPositionY() const { return offsetY; }
|
||||
|
||||
void Draw(const Point& screenPos) override;
|
||||
void XTick(float dt) override;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "OptionsView.h"
|
||||
#include "OptionsModel.h"
|
||||
#include "gui/game/GameModel.h"
|
||||
|
||||
#include "Controller.h"
|
||||
|
||||
@ -111,6 +112,11 @@ void OptionsController::Exit()
|
||||
HasExited = true;
|
||||
}
|
||||
|
||||
void OptionsController::NotifyKeyBindingsChanged()
|
||||
{
|
||||
gModel->NotifyKeyBindingsChanged();
|
||||
}
|
||||
|
||||
|
||||
OptionsController::~OptionsController()
|
||||
{
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
void SetShowAvatars(bool showAvatars);
|
||||
void SetMouseClickrequired(bool mouseClickRequired);
|
||||
void SetIncludePressure(bool includePressure);
|
||||
void NotifyKeyBindingsChanged();
|
||||
|
||||
void Exit();
|
||||
OptionsView * GetView();
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "gui/interface/DropDown.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "gui/interface/Checkbox.h"
|
||||
#include "keyboardbindings/KeyboardBindingsView.h"
|
||||
#include "keyboardbindings/KeyboardBindingsController.h"
|
||||
|
||||
#include "graphics/Graphics.h"
|
||||
|
||||
@ -448,6 +450,29 @@ OptionsView::OptionsView():
|
||||
tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||
tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||
scrollPanel->AddChild(tempLabel);
|
||||
currentY+=20;
|
||||
|
||||
class KeyboardBindingsAction: public ui::ButtonAction
|
||||
{
|
||||
public:
|
||||
KeyboardBindingsAction() { }
|
||||
void ActionCallback(ui::Button * sender) override
|
||||
{
|
||||
OptionsView* v = (OptionsView*) sender->GetParentWindow();
|
||||
KeyboardBindingsController* keyboardBindingsController = new KeyboardBindingsController(v->c);
|
||||
ui::Engine::Ref().ShowWindow(keyboardBindingsController->GetView());
|
||||
}
|
||||
};
|
||||
|
||||
ui::Button * keyboardBindingsButton = new ui::Button(ui::Point(8, currentY), ui::Point(130, 16), "Open Keyboard Bindings");
|
||||
keyboardBindingsButton->SetActionCallback(new KeyboardBindingsAction());
|
||||
scrollPanel->AddChild(keyboardBindingsButton);
|
||||
|
||||
tempLabel = new ui::Label(ui::Point(keyboardBindingsButton->Position.X+keyboardBindingsButton->Size.X+3, currentY), ui::Point(1, 16), "\bg- Change the keyboard bindings");
|
||||
autowidth(tempLabel);
|
||||
tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||
tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||
scrollPanel->AddChild(tempLabel);
|
||||
|
||||
class CloseAction: public ui::ButtonAction
|
||||
{
|
||||
|
@ -0,0 +1,97 @@
|
||||
#include "KeyboardBindingsController.h"
|
||||
|
||||
#include "KeyboardBindingsView.h"
|
||||
#include "Controller.h"
|
||||
#include "client/Client.h"
|
||||
#include "../OptionsController.h"
|
||||
#include "KeyboardBindingsModel.h"
|
||||
|
||||
KeyboardBindingsController::KeyboardBindingsController(OptionsController* _parent):
|
||||
HasExited(false)
|
||||
{
|
||||
parent = _parent;
|
||||
view = new KeyboardBindingsView();
|
||||
model = new KeyboardBindingsModel();
|
||||
model->AddObserver(view);
|
||||
view->AttachController(this);
|
||||
LoadBindingPrefs();
|
||||
view->BuildKeyBindingsListView();
|
||||
}
|
||||
|
||||
KeyboardBindingsView* KeyboardBindingsController::GetView()
|
||||
{
|
||||
return view;
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::CreateModel(BindingModel _model)
|
||||
{
|
||||
model->CreateModel(_model);
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::Save()
|
||||
{
|
||||
model->Save();
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::ChangeModel(BindingModel _model)
|
||||
{
|
||||
model->RemoveModelByIndex(_model.index);
|
||||
model->AddModel(_model);
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::Exit()
|
||||
{
|
||||
view->CloseActiveWindow();
|
||||
parent->NotifyKeyBindingsChanged();
|
||||
HasExited = true;
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::LoadBindingPrefs()
|
||||
{
|
||||
model->LoadBindingPrefs();
|
||||
}
|
||||
|
||||
std::vector<BindingModel> KeyboardBindingsController::GetBindingPrefs()
|
||||
{
|
||||
return model->GetBindingPrefs();
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::NotifyBindingsChanged()
|
||||
{
|
||||
bool hasConflict = model->HasConflictingCombo();
|
||||
model->NotifyBindingsChanged(hasConflict);
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::ForceHasConflict()
|
||||
{
|
||||
view->OnKeyCombinationChanged(true);
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::NotifyKeyReleased()
|
||||
{
|
||||
view->OnKeyReleased();
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::PopBindingByFunctionId(int32_t functionId)
|
||||
{
|
||||
model->PopBindingByFunctionId(functionId);
|
||||
}
|
||||
|
||||
void KeyboardBindingsController::ResetToDefaults()
|
||||
{
|
||||
model->WriteDefaultPrefs(true);
|
||||
}
|
||||
|
||||
bool KeyboardBindingsController::FunctionHasShortcut(int32_t functionId)
|
||||
{
|
||||
return model->FunctionHasShortcut(functionId);
|
||||
}
|
||||
|
||||
KeyboardBindingsController::~KeyboardBindingsController()
|
||||
{
|
||||
view->CloseActiveWindow();
|
||||
delete view;
|
||||
delete callback;
|
||||
delete model;
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
#ifndef KEYBOARDBINDINGSCONTROLLER_H
|
||||
#define KEYBOARDBINDINGSCONTROLLER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class ControllerCallback;
|
||||
class KeyboardBindingsView;
|
||||
class GameModel;
|
||||
class OptionsController;
|
||||
class KeyboardBindingsModel;
|
||||
struct BindingModel;
|
||||
|
||||
class KeyboardBindingsController
|
||||
{
|
||||
ControllerCallback * callback;
|
||||
KeyboardBindingsView* view;
|
||||
KeyboardBindingsModel* model;
|
||||
OptionsController* parent;
|
||||
public:
|
||||
bool HasExited;
|
||||
KeyboardBindingsController(OptionsController* _parent);
|
||||
void Exit();
|
||||
KeyboardBindingsView * GetView();
|
||||
virtual ~KeyboardBindingsController();
|
||||
void AddModel(BindingModel model);
|
||||
void CreateModel(BindingModel model);
|
||||
void ChangeModel(BindingModel model);
|
||||
void Save();
|
||||
void ForceHasConflict();
|
||||
void NotifyKeyReleased();
|
||||
void OnKeyReleased();
|
||||
void NotifyBindingsChanged();
|
||||
void PopBindingByFunctionId(int32_t functionId);
|
||||
bool FunctionHasShortcut(int32_t functionId);
|
||||
void ResetToDefaults();
|
||||
|
||||
void LoadBindingPrefs();
|
||||
std::vector<BindingModel> GetBindingPrefs();
|
||||
};
|
||||
|
||||
#endif /* KEYBOARDBINDINGSCONTROLLER_H */
|
152
src/gui/options/keyboardbindings/KeyboardBindingsMap.h
Normal file
152
src/gui/options/keyboardbindings/KeyboardBindingsMap.h
Normal file
@ -0,0 +1,152 @@
|
||||
#ifndef KEYBOARDBINDINGSMAP_H
|
||||
#define KEYBOARDBINDINGSMAP_H
|
||||
|
||||
#include "common/String.h"
|
||||
|
||||
typedef struct KeyboardBindingMap
|
||||
{
|
||||
uint32_t id;
|
||||
String description;
|
||||
int32_t functionId;
|
||||
} KeyboardBindingMap;
|
||||
|
||||
typedef struct DefaultKeyboardBindingMap
|
||||
{
|
||||
ByteString keyCombo;
|
||||
uint32_t bindingId; // KeyboardBindingMap id
|
||||
} DefaultKeyboardBindingMap;
|
||||
|
||||
static KeyboardBindingMap keyboardBindingFunctionMap[] =
|
||||
{
|
||||
{ 0x00, "Reload Simulation", 0 },
|
||||
{ 0x01, "Open Element Search", 1 },
|
||||
{ 0x02, "Toggle Air Mode", 2 },
|
||||
{ 0x03, "Toggle Heat", 3 },
|
||||
{ 0x04, "Toggle Newtonian Gravity", 4 },
|
||||
{ 0x05, "Open Stamps", 5 },
|
||||
{ 0x06, "Invert Air Simulation", 6 },
|
||||
{ 0x07, "Pause Simulation", 7 },
|
||||
{ 0x08, "Enable Zoom", 8 },
|
||||
{ 0x09, "Undo", 9 },
|
||||
{ 0x0A, "Redo", 10 },
|
||||
{ 0x0B, "Property Tool", 11 },
|
||||
{ 0x0C, "Property Tool", 11 },
|
||||
{ 0x0D, "Screenshot", 12 },
|
||||
{ 0x0E, "Toggle Debug HUD", 13 },
|
||||
{ 0x0F, "Save Authorship Info", 14 },
|
||||
{ 0x10, "Reload Simulation", 0 },
|
||||
{ 0x11, "Frame Step", 15 },
|
||||
{ 0x12, "Find Mode", 16 },
|
||||
{ 0x13, "Show Gravity Grid", 17 },
|
||||
{ 0x14, "Increase Gravity Grid Size", 18 },
|
||||
{ 0x15, "Decrease Gravity Grid Size", 19 },
|
||||
{ 0x16, "Toggle Intro Text", 20 },
|
||||
{ 0x17, "Toggle Intro Text", 20 },
|
||||
{ 0x18, "Toggle HUD", 21 },
|
||||
{ 0x19, "Toggle Decorations Layer", 22 },
|
||||
{ 0x1A, "Toggle Decoration Tool", 23 },
|
||||
{ 0x1B, "Redo", 10 },
|
||||
{ 0x1C, "Quit", 24 },
|
||||
{ 0x1D, "Quit", 24 },
|
||||
{ 0x1E, "Reset Spark", 25 },
|
||||
{ 0x1F, "Reset Air", 26 },
|
||||
{ 0x20, "Copy", 27 },
|
||||
{ 0x21, "Cut", 28 },
|
||||
{ 0x22, "Paste", 29 },
|
||||
{ 0x23, "Stamp Tool", 30 },
|
||||
{ 0x24, "Increase Brush Size", 31 },
|
||||
{ 0x25, "Decrease Brush Size", 32 },
|
||||
{ 0x26, "Install Game", 33 },
|
||||
{ 0x27, "Toggle Replace Mode", 34 },
|
||||
{ 0x28, "Toggle Specific Delete Mode", 35 },
|
||||
{ 0x29, "Toggle Console", 36 }
|
||||
};
|
||||
|
||||
enum KeyboardBindingFunction
|
||||
{
|
||||
RELOAD_SIMULATION,
|
||||
OPEN_ELEMENT_SEARCH,
|
||||
TOGGLE_AIR_MODE,
|
||||
TOGGLE_HEAT,
|
||||
TOGGLE_NEWTONIAN_GRAVITY,
|
||||
OPEN_STAMPS,
|
||||
INVERT_AIR_SIMULATION,
|
||||
PAUSE_SIMULATION,
|
||||
ENABLE_ZOOM,
|
||||
UNDO,
|
||||
REDO,
|
||||
PROPERTY_TOOL,
|
||||
SCREENSHOT,
|
||||
TOGGLE_DEBUG_HUD,
|
||||
SAVE_AUTHORSHIP_INFO,
|
||||
FRAME_STEP,
|
||||
FIND_MODE,
|
||||
SHOW_GRAVITY_GRID,
|
||||
INCREASE_GRAVITY_GRID_SIZE,
|
||||
DECREASE_GRAVITY_GRID_SIZE,
|
||||
TOGGLE_INTRO_TEXT,
|
||||
TOGGLE_HUD,
|
||||
TOGGLE_DECORATIONS_LAYER,
|
||||
TOGGLE_DECORATION_TOOL,
|
||||
QUIT,
|
||||
RESET_SPARK,
|
||||
RESET_AIR,
|
||||
COPY,
|
||||
CUT,
|
||||
PASTE,
|
||||
STAMP_TOOL,
|
||||
INCREASE_BRUSH_SIZE,
|
||||
DECREASE_BRUSH_SIZE,
|
||||
INSTALL_GAME,
|
||||
TOGGLE_REPLACE_MODE,
|
||||
TOGGLE_SPECIFIC_DELETE_MODE,
|
||||
TOGGLE_CONSOLE
|
||||
};
|
||||
|
||||
static DefaultKeyboardBindingMap defaultKeyboardBindingMapArray[] =
|
||||
{
|
||||
{ "0+62", 0x00 },
|
||||
{ "0+8", 0x01 },
|
||||
{ "0+28", 0x02 },
|
||||
{ "0+24", 0x03 },
|
||||
{ "0+17", 0x04 },
|
||||
{ "0+14", 0x05 },
|
||||
{ "0+12", 0x06 },
|
||||
{ "0+44", 0x07 },
|
||||
{ "0+29", 0x08 },
|
||||
{ "1+29", 0x09 },
|
||||
{ "5+29", 0x0A },
|
||||
{ "5+69", 0x0B },
|
||||
{ "1+69", 0x0C },
|
||||
{ "0+69", 0x0D },
|
||||
{ "0+60", 0x0E },
|
||||
{ "0+4", 0x0F },
|
||||
{ "1+21", 0x10 },
|
||||
{ "0+9", 0x11 },
|
||||
{ "1+9", 0x12 },
|
||||
{ "1+10", 0x13 },
|
||||
{ "0+10", 0x14 },
|
||||
{ "4+10", 0x15 },
|
||||
{ "0+58", 0x16 },
|
||||
{ "1+11", 0x17 },
|
||||
{ "0+11", 0x18 },
|
||||
{ "1+5", 0x19 },
|
||||
{ "0+5", 0x1A },
|
||||
{ "1+28", 0x1B },
|
||||
{ "0+41", 0x1C },
|
||||
{ "0+20", 0x1D },
|
||||
{ "1+46", 0x1E },
|
||||
{ "0+46", 0x1F },
|
||||
{ "1+6", 0x20 },
|
||||
{ "1+27", 0x21 },
|
||||
{ "1+25", 0x22 },
|
||||
{ "0+15", 0x23 },
|
||||
{ "0+48", 0x24 },
|
||||
{ "0+47", 0x25 },
|
||||
{ "1+12", 0x26 },
|
||||
{ "1+51", 0x27 },
|
||||
{ "0+51", 0x28 },
|
||||
{ "0+53", 0x29 }
|
||||
};
|
||||
|
||||
#endif
|
359
src/gui/options/keyboardbindings/KeyboardBindingsModel.cpp
Normal file
359
src/gui/options/keyboardbindings/KeyboardBindingsModel.cpp
Normal file
@ -0,0 +1,359 @@
|
||||
#include "KeyboardBindingsModel.h"
|
||||
#include "client/Client.h"
|
||||
#include "SDLCompat.h"
|
||||
#include "KeyboardBindingsMap.h"
|
||||
#include <algorithm>
|
||||
#include "KeyboardBindingsView.h"
|
||||
|
||||
void KeyboardBindingsModel::WriteDefaultFuncArray(bool force)
|
||||
{
|
||||
if (force)
|
||||
Client::Ref().ClearPref(ByteString(KEYBOARDBINDING_FUNCS_PREF));
|
||||
|
||||
for (auto defaultBinding : defaultKeyboardBindingMapArray)
|
||||
{
|
||||
int32_t functionId;
|
||||
String description;
|
||||
for (auto functions : keyboardBindingFunctionMap)
|
||||
{
|
||||
if (functions.id == defaultBinding.bindingId)
|
||||
{
|
||||
functionId = functions.functionId;
|
||||
description = functions.description;
|
||||
}
|
||||
}
|
||||
|
||||
ByteString pref = ByteString(KEYBOARDBINDING_FUNCS_PREF) + ByteString(".") + ByteString(functionId);
|
||||
bool functionExists = Client::Ref().GetPrefJson(pref, Json::nullValue) != Json::nullValue;
|
||||
|
||||
if (!force && functionExists)
|
||||
continue;
|
||||
|
||||
Json::Value prefValue;
|
||||
|
||||
prefValue["hasShortcut"] = true;
|
||||
prefValue["functionId"] = functionId;
|
||||
Client::Ref().SetPref(pref, prefValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::WriteDefaultPrefs(bool force)
|
||||
{
|
||||
// Load temporary bindings into memory
|
||||
// this is so we can add in any new axctions
|
||||
// from the KeyboardBindingsMap into our prefs
|
||||
LoadBindingPrefs();
|
||||
|
||||
if (force)
|
||||
Client::Ref().ClearPref(ByteString(KEYBOARDBINDING_PREF));
|
||||
|
||||
WriteDefaultFuncArray(force);
|
||||
|
||||
for (auto defaultBinding : defaultKeyboardBindingMapArray)
|
||||
{
|
||||
int32_t functionId;
|
||||
String description;
|
||||
for (auto functions : keyboardBindingFunctionMap)
|
||||
{
|
||||
if (functions.id == defaultBinding.bindingId)
|
||||
{
|
||||
functionId = functions.functionId;
|
||||
description = functions.description;
|
||||
}
|
||||
}
|
||||
|
||||
ByteString pref = ByteString(KEYBOARDBINDING_PREF) + ByteString(".") + defaultBinding.keyCombo;
|
||||
Json::Value prefValue;
|
||||
|
||||
// if we not forcing then check if the function is already set up as a pref
|
||||
// if it is then bail the current iteration
|
||||
if (!force)
|
||||
{
|
||||
if (bindingPrefs.size() > 0)
|
||||
{
|
||||
for (auto prefBinding : bindingPrefs)
|
||||
{
|
||||
if (prefBinding.functionId == functionId)
|
||||
goto end; // evil but necessary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prefValue["description"] = description.ToUtf8();
|
||||
prefValue["functionId"] = functionId;
|
||||
Client::Ref().SetPref(pref, prefValue);
|
||||
|
||||
end:;
|
||||
}
|
||||
|
||||
// force is from a user action so don't write into store
|
||||
// until user hits OK
|
||||
if (!force)
|
||||
Client::Ref().WritePrefs();
|
||||
|
||||
LoadBindingPrefs();
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::LoadBindingPrefs()
|
||||
{
|
||||
Json::Value bindings = Client::Ref().GetPrefJson(KEYBOARDBINDING_PREF);
|
||||
bindingPrefs.clear();
|
||||
|
||||
if (bindings != Json::nullValue)
|
||||
{
|
||||
Json::Value::Members keyComboJson = bindings.getMemberNames();
|
||||
uint32_t index = 0;
|
||||
|
||||
for (auto& member : keyComboJson)
|
||||
{
|
||||
ByteString keyCombo(member);
|
||||
ByteString pref = ByteString(KEYBOARDBINDING_PREF) + "." + keyCombo;
|
||||
Json::Value result = Client::Ref().GetPrefJson(pref);
|
||||
|
||||
if (result != Json::nullValue)
|
||||
{
|
||||
BindingModel model;
|
||||
std::pair<uint32_t, uint32_t> p = GetModifierAndScanFromString(keyCombo);
|
||||
model.modifier = p.first;
|
||||
model.scan = p.second;
|
||||
model.functionId = result["functionId"].asInt();
|
||||
model.description = ByteString(result["description"].asString()).FromUtf8();
|
||||
model.index = index;
|
||||
bindingPrefs.push_back(model);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t>
|
||||
KeyboardBindingsModel::GetModifierAndScanFromString(ByteString str)
|
||||
{
|
||||
uint32_t modifier = 0;
|
||||
uint32_t scan = 0;
|
||||
|
||||
if (str == "NULL")
|
||||
{
|
||||
scan = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteString::Split split = str.SplitBy("+");
|
||||
|
||||
// not the last int so its a modifier
|
||||
ByteString modString = split.Before();
|
||||
|
||||
modifier |= (std::stoi(modString) & BINDING_MASK);
|
||||
scan = std::stoi(split.After());
|
||||
}
|
||||
|
||||
return std::make_pair(modifier, scan);
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::TurnOffFunctionShortcut(int32_t functionId)
|
||||
{
|
||||
ByteString pref = ByteString(KEYBOARDBINDING_FUNCS_PREF) + ByteString(".") + ByteString(functionId)
|
||||
+ ByteString(".hasShortcut");
|
||||
|
||||
Client::Ref().SetPref(pref, false);
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::TurnOnFunctionShortcut(int32_t functionId)
|
||||
{
|
||||
ByteString pref = ByteString(KEYBOARDBINDING_FUNCS_PREF) + ByteString(".") + ByteString(functionId)
|
||||
+ ByteString(".hasShortcut");
|
||||
|
||||
Client::Ref().SetPref(pref, true);
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::RemoveModelByIndex(uint32_t index)
|
||||
{
|
||||
std::vector<BindingModel>::iterator it = bindingPrefs.begin();
|
||||
|
||||
while(it != bindingPrefs.end())
|
||||
{
|
||||
auto& pref = *it;
|
||||
if (pref.index == index)
|
||||
{
|
||||
bindingPrefs.erase(it);
|
||||
return;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::CreateModel(BindingModel model)
|
||||
{
|
||||
// if the function has no shortcut then just turn it on
|
||||
if (!FunctionHasShortcut(model.functionId))
|
||||
{
|
||||
TurnOnFunctionShortcut(model.functionId);
|
||||
return;
|
||||
}
|
||||
|
||||
// index is just an session based id that we use
|
||||
// to identify removals/changes
|
||||
// so whenever a new model is created we just set it to the
|
||||
// size of the container
|
||||
model.index = bindingPrefs.size();
|
||||
bindingPrefs.push_back(model);
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::AddModel(BindingModel model)
|
||||
{
|
||||
bindingPrefs.push_back(model);
|
||||
TurnOnFunctionShortcut(model.functionId);
|
||||
bool hasConflict = HasConflictingCombo();
|
||||
NotifyBindingsChanged(hasConflict);
|
||||
}
|
||||
|
||||
bool KeyboardBindingsModel::FunctionHasShortcut(int32_t functionId)
|
||||
{
|
||||
ByteString pref = ByteString(KEYBOARDBINDING_FUNCS_PREF) + ByteString(".") + ByteString(functionId)
|
||||
+ ByteString(".hasShortcut");
|
||||
|
||||
return Client::Ref().GetPrefBool(pref, false);
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::Save()
|
||||
{
|
||||
Client::Ref().ClearPref(KEYBOARDBINDING_PREF);
|
||||
|
||||
for (auto& binding : bindingPrefs)
|
||||
{
|
||||
ByteString mod(std::to_string(binding.modifier));
|
||||
ByteString scan(std::to_string(binding.scan));
|
||||
ByteString pref = ByteString(KEYBOARDBINDING_PREF) + ByteString(".") + mod + ByteString("+") + scan;
|
||||
|
||||
Json::Value val;
|
||||
val["functionId"] = binding.functionId;
|
||||
val["description"] = binding.description.ToUtf8();
|
||||
Client::Ref().SetPref(pref, val);
|
||||
}
|
||||
|
||||
Client::Ref().WritePrefs();
|
||||
}
|
||||
|
||||
int32_t KeyboardBindingsModel::GetFunctionForBinding(int scan, bool shift, bool ctrl, bool alt)
|
||||
{
|
||||
uint32_t modifier = 0;
|
||||
|
||||
if (ctrl)
|
||||
modifier |= BINDING_CTRL;
|
||||
|
||||
if (alt)
|
||||
modifier |= BINDING_ALT;
|
||||
|
||||
if (shift)
|
||||
modifier |= BINDING_SHIFT;
|
||||
|
||||
auto it = std::find_if(bindingPrefs.begin(), bindingPrefs.end(), [modifier, scan](BindingModel m)
|
||||
{
|
||||
return m.modifier == modifier && m.scan == scan;
|
||||
});
|
||||
|
||||
if (it != bindingPrefs.end())
|
||||
{
|
||||
BindingModel binding = *it;
|
||||
|
||||
if (FunctionHasShortcut(binding.functionId))
|
||||
return binding.functionId;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Here we pop off a hotkey if the user clicks delete
|
||||
* however if we are on the last remaining hotkey
|
||||
* then we turn off hasShortcut for the associated function
|
||||
* so it renders as *No Shortcut* on the view
|
||||
*/
|
||||
void KeyboardBindingsModel::PopBindingByFunctionId(int32_t functionId)
|
||||
{
|
||||
std::sort(bindingPrefs.begin(), bindingPrefs.end(), [](BindingModel a, BindingModel b)
|
||||
{
|
||||
return a.index > b.index;
|
||||
});
|
||||
|
||||
std::vector<BindingModel> v;
|
||||
for (auto b : bindingPrefs)
|
||||
{
|
||||
if (b.functionId == functionId)
|
||||
v.push_back(b);
|
||||
}
|
||||
|
||||
if (v.size() == 1)
|
||||
{
|
||||
auto it = std::find(bindingPrefs.begin(), bindingPrefs.end(), v[0]);
|
||||
TurnOffFunctionShortcut((*it).functionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = bindingPrefs.begin();
|
||||
while (it != bindingPrefs.end())
|
||||
{
|
||||
if ((*it).functionId == functionId)
|
||||
{
|
||||
bindingPrefs.erase(it);
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String KeyboardBindingsModel::GetDisplayForModel(BindingModel model)
|
||||
{
|
||||
return model.description;
|
||||
}
|
||||
|
||||
bool KeyboardBindingsModel::HasConflictingCombo()
|
||||
{
|
||||
for (auto& binding : bindingPrefs)
|
||||
{
|
||||
// if we have any new bindings then we
|
||||
// need to return a conflict until
|
||||
// the user types out a binding
|
||||
if (binding.isNew)
|
||||
return true;
|
||||
|
||||
// if the current binding has no shortcut then skip
|
||||
if (!FunctionHasShortcut(binding.functionId))
|
||||
continue;
|
||||
|
||||
// if key combo appears twice then there is a conflicting combo
|
||||
auto iter = std::find(bindingPrefs.begin(), bindingPrefs.end(), binding);
|
||||
if (iter != bindingPrefs.end())
|
||||
{
|
||||
// if this time round we don't have a shortcut either
|
||||
// then we can safely continue because this means
|
||||
// we don't have a conflict for the current binding
|
||||
if (!FunctionHasShortcut((*iter).functionId))
|
||||
continue;
|
||||
|
||||
iter++;
|
||||
iter = std::find(iter, bindingPrefs.end(), binding);
|
||||
if (iter != bindingPrefs.end())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::AddObserver(KeyboardBindingsView* observer)
|
||||
{
|
||||
observers.push_back(observer);
|
||||
}
|
||||
|
||||
void KeyboardBindingsModel::NotifyBindingsChanged(bool hasConflict)
|
||||
{
|
||||
for (auto& observer : observers)
|
||||
{
|
||||
observer->OnKeyCombinationChanged(hasConflict);
|
||||
}
|
||||
}
|
74
src/gui/options/keyboardbindings/KeyboardBindingsModel.h
Normal file
74
src/gui/options/keyboardbindings/KeyboardBindingsModel.h
Normal file
@ -0,0 +1,74 @@
|
||||
#ifndef KEYBOARDBINDINGSMODEL_H
|
||||
#define KEYBOARDBINDINGSMODEL_H
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include "common/String.h"
|
||||
|
||||
#define KEYBOARDBINDING_PREF "KeyboardBindings"
|
||||
#define KEYBOARDBINDING_FUNCS_PREF "KeyboardBindingFunctions"
|
||||
|
||||
#define BINDING_MASK 0x07
|
||||
#define BINDING_CTRL 0x01
|
||||
#define BINDING_ALT 0x02
|
||||
#define BINDING_SHIFT 0x04
|
||||
|
||||
struct BindingModel
|
||||
{
|
||||
uint32_t modifier;
|
||||
uint32_t scan;
|
||||
int32_t functionId;
|
||||
String description;
|
||||
uint32_t index;
|
||||
bool isNew;
|
||||
bool noShortcut;
|
||||
|
||||
BindingModel() : noShortcut(false), isNew(false){};
|
||||
|
||||
bool operator==(const BindingModel& other) const
|
||||
{
|
||||
return modifier == other.modifier && scan == other.scan;
|
||||
}
|
||||
|
||||
bool operator< (const BindingModel &other) const
|
||||
{
|
||||
if (description == other.description)
|
||||
return index < other.index;
|
||||
|
||||
return description < other.description;
|
||||
}
|
||||
};
|
||||
|
||||
class KeyboardBindingsView;
|
||||
|
||||
class KeyboardBindingsModel
|
||||
{
|
||||
public:
|
||||
KeyboardBindingsModel(){}
|
||||
void WriteDefaultPrefs(bool force = false); // true if user clicks reset to defaults
|
||||
|
||||
inline std::vector<BindingModel> GetBindingPrefs() const { return bindingPrefs; }
|
||||
void LoadBindingPrefs();
|
||||
void Save();
|
||||
void RemoveModelByIndex(uint32_t index);
|
||||
void AddModel(BindingModel model);
|
||||
void CreateModel(BindingModel model);
|
||||
String GetDisplayForModel(BindingModel model);
|
||||
void AddObserver(KeyboardBindingsView* observer);
|
||||
void NotifyBindingsChanged(bool hasConflict);
|
||||
bool HasConflictingCombo();
|
||||
void PopBindingByFunctionId(int32_t functionId);
|
||||
void WriteDefaultFuncArray(bool force = false);
|
||||
bool FunctionHasShortcut(int32_t functionId);
|
||||
int32_t GetFunctionForBinding(int scan, bool shift, bool ctrl, bool alt);
|
||||
|
||||
protected:
|
||||
void TurnOffFunctionShortcut(int32_t functionId);
|
||||
void TurnOnFunctionShortcut(int32_t functionId);
|
||||
|
||||
std::vector<KeyboardBindingsView*> observers;
|
||||
std::vector<BindingModel> bindingPrefs;
|
||||
std::pair<uint32_t, uint32_t> GetModifierAndScanFromString(ByteString str);
|
||||
};
|
||||
|
||||
#endif // KEYBOARDBINDINGSMODEL_H
|
120
src/gui/options/keyboardbindings/KeyboardBindingsTextbox.cpp
Normal file
120
src/gui/options/keyboardbindings/KeyboardBindingsTextbox.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
#include "KeyboardBindingsTextbox.h"
|
||||
|
||||
#include "SDLCompat.h"
|
||||
#include "gui/interface/Window.h"
|
||||
#include "client/Client.h"
|
||||
#include "KeyboardBindingsModel.h"
|
||||
#include "KeyboardBindingsController.h"
|
||||
|
||||
KeyboardBindingsTextbox::KeyboardBindingsTextbox(ui::Point position, ui::Point size) :
|
||||
ui::Textbox(position, size)
|
||||
{
|
||||
// reasonable defaults
|
||||
SetTextColour(ui::Colour(255, 255, 255));
|
||||
Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
|
||||
Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||
}
|
||||
|
||||
void KeyboardBindingsTextbox::OnMouseClick(int x, int y, unsigned button)
|
||||
{
|
||||
prevKey = GetText();
|
||||
SetText("");
|
||||
c->ForceHasConflict();
|
||||
}
|
||||
|
||||
void KeyboardBindingsTextbox::AttachController(KeyboardBindingsController* _c)
|
||||
{
|
||||
c = _c;
|
||||
}
|
||||
|
||||
void KeyboardBindingsTextbox::SetModel(BindingModel _model)
|
||||
{
|
||||
model = _model;
|
||||
}
|
||||
|
||||
void KeyboardBindingsTextbox::SetTextToPrevious()
|
||||
{
|
||||
SetText(prevKey);
|
||||
}
|
||||
|
||||
void KeyboardBindingsTextbox::SetTextFromModifierAndScan(uint32_t modifier, uint32_t scan)
|
||||
{
|
||||
ByteString modDisplay;
|
||||
|
||||
if (modifier & BINDING_CTRL)
|
||||
{
|
||||
modDisplay += "CTRL+";
|
||||
}
|
||||
|
||||
if (modifier & BINDING_ALT)
|
||||
{
|
||||
modDisplay += "ALT+";
|
||||
}
|
||||
|
||||
if (modifier & BINDING_SHIFT)
|
||||
{
|
||||
modDisplay += "SHIFT+";
|
||||
}
|
||||
|
||||
const char* scanDisplay = SDL_GetScancodeName((SDL_Scancode) scan);
|
||||
ByteString keyNameDisplay(scanDisplay);
|
||||
keyNameDisplay = modDisplay + keyNameDisplay.ToUpper();
|
||||
|
||||
SetText(keyNameDisplay.FromUtf8());
|
||||
}
|
||||
|
||||
void KeyboardBindingsTextbox::OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
|
||||
{
|
||||
ui::Textbox::OnKeyRelease(key, scan, repeat, shift, ctrl, alt);
|
||||
|
||||
uint32_t mod = 0x00;
|
||||
ByteString modDisplay = "";
|
||||
|
||||
if (ctrl)
|
||||
{
|
||||
mod |= BINDING_CTRL;
|
||||
modDisplay += "CTRL+";
|
||||
}
|
||||
|
||||
if (alt)
|
||||
{
|
||||
mod |= BINDING_ALT;
|
||||
modDisplay += "ALT+";
|
||||
}
|
||||
|
||||
if (shift)
|
||||
{
|
||||
mod |= BINDING_SHIFT;
|
||||
modDisplay += "SHIFT+";
|
||||
}
|
||||
|
||||
const char* scanDisplay = SDL_GetScancodeName((SDL_Scancode) scan);
|
||||
ByteString keyNameDisplay(scanDisplay);
|
||||
keyNameDisplay = modDisplay + keyNameDisplay.ToUpper();
|
||||
|
||||
if (!scan)
|
||||
{
|
||||
SetText(prevKey);
|
||||
return;
|
||||
}
|
||||
|
||||
SetText(keyNameDisplay.FromUtf8());
|
||||
GetParentWindow()->FocusComponent(NULL);
|
||||
|
||||
BindingModel newModel;
|
||||
newModel.modifier = mod;
|
||||
newModel.scan = (uint32_t) scan;
|
||||
newModel.functionId = model.functionId;
|
||||
newModel.description = model.description;
|
||||
newModel.index = model.index;
|
||||
newModel.isNew = false;
|
||||
newModel.noShortcut = false;
|
||||
|
||||
c->ChangeModel(newModel);
|
||||
|
||||
model = newModel;
|
||||
|
||||
// we notify the controller so the view can recover all empty textboxes
|
||||
// should the user carelessly click about
|
||||
c->NotifyKeyReleased();
|
||||
}
|
31
src/gui/options/keyboardbindings/KeyboardBindingsTextbox.h
Normal file
31
src/gui/options/keyboardbindings/KeyboardBindingsTextbox.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef KEYBOARDBINDINGSTEXTBOX_H
|
||||
#define KEYBOARDBINDINGSTEXTBOX_H
|
||||
|
||||
#include "gui/interface/Textbox.h"
|
||||
#include "KeyboardBindingsModel.h"
|
||||
|
||||
class KeyboardBindingsController;
|
||||
|
||||
class KeyboardBindingsTextbox: public ui::Textbox
|
||||
{
|
||||
public:
|
||||
KeyboardBindingsTextbox(ui::Point position, ui::Point size);
|
||||
|
||||
void OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);
|
||||
void OnMouseClick(int x, int y, unsigned button);
|
||||
|
||||
void SetModel(BindingModel _model);
|
||||
void SetTextFromModifierAndScan(uint32_t modifier, uint32_t scan);
|
||||
void SetTextToPrevious();
|
||||
|
||||
void OnTextInput(String text) {}
|
||||
|
||||
void AttachController(KeyboardBindingsController* _c);
|
||||
|
||||
protected:
|
||||
String prevKey;
|
||||
KeyboardBindingsController* c;
|
||||
BindingModel model;
|
||||
};
|
||||
|
||||
#endif /* KEYBOARDBINDINGSTEXTBOX_H */
|
290
src/gui/options/keyboardbindings/KeyboardBindingsView.cpp
Normal file
290
src/gui/options/keyboardbindings/KeyboardBindingsView.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
#include "KeyboardBindingsView.h"
|
||||
|
||||
#include "gui/interface/Button.h"
|
||||
#include "gui/interface/Label.h"
|
||||
#include "gui/interface/DropDown.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "gui/interface/Checkbox.h"
|
||||
#include "gui/interface/ScrollPanel.h"
|
||||
#include "gui/Style.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "KeyboardBindingsMap.h"
|
||||
#include "KeyboardBindingsTextbox.h"
|
||||
#include "KeyboardBindingsModel.h"
|
||||
#include "KeyboardBindingsController.h"
|
||||
#include "client/Client.h"
|
||||
#include <vector>
|
||||
|
||||
KeyboardBindingsView::KeyboardBindingsView() :
|
||||
ui::Window(ui::Point(-1, -1), ui::Point(320, 340)) {
|
||||
|
||||
ui::Label * tempLabel = new ui::Label(ui::Point(4, 1), ui::Point(Size.X / 2, 22), "Keyboard Bindings");
|
||||
tempLabel->SetTextColour(style::Colour::InformationTitle);
|
||||
tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||
tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||
AddComponent(tempLabel);
|
||||
|
||||
class ResetDefaultsAction: public ui::ButtonAction
|
||||
{
|
||||
public:
|
||||
KeyboardBindingsView * v;
|
||||
ResetDefaultsAction(KeyboardBindingsView * v_) { v = v_; }
|
||||
void ActionCallback(ui::Button * sender) override
|
||||
{
|
||||
v->c->ResetToDefaults();
|
||||
v->BuildKeyBindingsListView();
|
||||
v->c->NotifyBindingsChanged();
|
||||
}
|
||||
};
|
||||
|
||||
ui::Button* resetDefaults = new ui::Button(ui::Point(Size.X - 150, 5), ui::Point(140, 18), "Reset to Defaults");
|
||||
resetDefaults->SetActionCallback(new ResetDefaultsAction(this));
|
||||
AddComponent(resetDefaults);
|
||||
|
||||
conflictLabel = new ui::Label(ui::Point(4, resetDefaults->Size.Y + 10), ui::Point(Size.X / 2, 18), "Please resolve conflicts or empty bindings");
|
||||
conflictLabel->SetTextColour(style::Colour::ErrorTitle);
|
||||
conflictLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||
conflictLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||
conflictLabel->Visible = false;
|
||||
AddComponent(conflictLabel);
|
||||
|
||||
class CloseAction: public ui::ButtonAction
|
||||
{
|
||||
public:
|
||||
KeyboardBindingsView * v;
|
||||
CloseAction(KeyboardBindingsView * v_) { v = v_; }
|
||||
void ActionCallback(ui::Button * sender) override
|
||||
{
|
||||
v->c->Save();
|
||||
v->c->Exit();
|
||||
}
|
||||
};
|
||||
|
||||
okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK");
|
||||
okayButton->SetActionCallback(new CloseAction(this));
|
||||
AddComponent(okayButton);
|
||||
SetCancelButton(okayButton);
|
||||
SetOkayButton(okayButton);
|
||||
scrollPanel = new ui::ScrollPanel(ui::Point(1, 50), ui::Point(Size.X-2, Size.Y-70));
|
||||
AddComponent(scrollPanel);
|
||||
}
|
||||
|
||||
void KeyboardBindingsView::ClearScrollPanel()
|
||||
{
|
||||
int count = scrollPanel->GetChildCount();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
auto com = scrollPanel->GetChild(i);
|
||||
scrollPanel->RemoveChild(com);
|
||||
RemoveComponent(com);
|
||||
}
|
||||
|
||||
RemoveComponent(scrollPanel);
|
||||
textboxes.clear();
|
||||
}
|
||||
|
||||
void KeyboardBindingsView::BuildKeyBindingsListView()
|
||||
{
|
||||
uint32_t currentY = 0;
|
||||
float scrollPos = scrollPanel->GetScrollPositionY();
|
||||
ClearScrollPanel();
|
||||
|
||||
scrollPanel = new ui::ScrollPanel(ui::Point(1, 50), ui::Point(Size.X-2, Size.Y-70));
|
||||
AddComponent(scrollPanel);
|
||||
|
||||
std::vector<BindingModel> bindingModel = c->GetBindingPrefs();
|
||||
std::sort(bindingModel.begin(), bindingModel.end());
|
||||
|
||||
for (int i = 0; i < bindingModel.size(); i++)
|
||||
{
|
||||
BindingModel& binding = bindingModel[i];
|
||||
|
||||
ui::Label * functionLabel = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X / 2, 16), binding.description);
|
||||
functionLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||
scrollPanel->AddChild(functionLabel);
|
||||
|
||||
KeyboardBindingsTextbox* textbox;
|
||||
ui::Label* noShortCutLabel;
|
||||
|
||||
bool hasBinding = true;
|
||||
int removeButtonPosX = 0;
|
||||
|
||||
if (!c->FunctionHasShortcut(binding.functionId))
|
||||
{
|
||||
hasBinding = false;
|
||||
noShortCutLabel = new ui::Label(ui::Point(functionLabel->Position.X + functionLabel->Size.X + 20, currentY), ui::Point(95, 16), "(No shortcut)");
|
||||
noShortCutLabel->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
|
||||
scrollPanel->AddChild(noShortCutLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
textbox = new KeyboardBindingsTextbox(ui::Point(functionLabel->Position.X + functionLabel->Size.X + 20, currentY), ui::Point(95, 16));
|
||||
textbox->SetTextFromModifierAndScan(binding.modifier, binding.scan);
|
||||
textbox->SetModel(binding);
|
||||
textbox->AttachController(c);
|
||||
textboxes.push_back(textbox);
|
||||
scrollPanel->AddChild(textbox);
|
||||
removeButtonPosX = textbox->Position.X + textbox->Size.X + 5;
|
||||
}
|
||||
|
||||
int addButtonPosX = functionLabel->Position.X + functionLabel->Size.X - 5;
|
||||
int addRemoveButtonsPosY = currentY;
|
||||
currentY += 20;
|
||||
|
||||
// add in all the bindings associated with the current functionId
|
||||
if (hasBinding)
|
||||
{
|
||||
auto it = bindingModel.begin() + i + 1;
|
||||
while (it != bindingModel.end())
|
||||
{
|
||||
BindingModel nextBinding = *it;
|
||||
if (nextBinding.functionId == binding.functionId)
|
||||
{
|
||||
KeyboardBindingsTextbox* tb = new KeyboardBindingsTextbox(ui::Point(functionLabel->Position.X + functionLabel->Size.X + 20, currentY), ui::Point(95, 16));
|
||||
if (!nextBinding.isNew)
|
||||
tb->SetTextFromModifierAndScan(nextBinding.modifier, nextBinding.scan);
|
||||
else
|
||||
tb->SetText("");
|
||||
|
||||
tb->SetModel(nextBinding);
|
||||
tb->AttachController(c);
|
||||
textboxes.push_back(tb);
|
||||
scrollPanel->AddChild(tb);
|
||||
currentY += 20;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the vector is sorted so once we hit unequality
|
||||
// in function id then it means we are onto the next function
|
||||
break;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
ui::Button* addButton = new ui::Button(ui::Point(addButtonPosX, addRemoveButtonsPosY), ui::Point(20, 16), "+", "Add a binding to this action");
|
||||
|
||||
scrollPanel->AddChild(addButton);
|
||||
|
||||
class AddBindingAction: public ui::ButtonAction
|
||||
{
|
||||
public:
|
||||
KeyboardBindingsView * v;
|
||||
int32_t functionId;
|
||||
String desc;
|
||||
|
||||
AddBindingAction(KeyboardBindingsView * v_, int32_t _functionId, String _desc)
|
||||
{
|
||||
v = v_;
|
||||
functionId = _functionId;
|
||||
desc = _desc;
|
||||
}
|
||||
|
||||
void ActionCallback(ui::Button * sender) override
|
||||
{
|
||||
auto modelArr = v->c->GetBindingPrefs();
|
||||
auto it = std::find_if(modelArr.begin(), modelArr.end(), [this](BindingModel b)
|
||||
{
|
||||
return b.functionId == functionId && b.isNew;
|
||||
});
|
||||
|
||||
// do not add more KBT's if we have an empty one on the
|
||||
// current function
|
||||
if (it != modelArr.end())
|
||||
return;
|
||||
|
||||
BindingModel model;
|
||||
model.isNew = true;
|
||||
model.functionId = functionId;
|
||||
model.description = desc; // for sorting
|
||||
v->c->CreateModel(model);
|
||||
v->BuildKeyBindingsListView();
|
||||
v->c->NotifyBindingsChanged();
|
||||
}
|
||||
};
|
||||
|
||||
class RemoveBindingAction: public ui::ButtonAction
|
||||
{
|
||||
public:
|
||||
KeyboardBindingsView * v;
|
||||
int32_t functionId;
|
||||
|
||||
RemoveBindingAction(KeyboardBindingsView * v_, int32_t _functionId)
|
||||
{
|
||||
v = v_;
|
||||
functionId = _functionId;
|
||||
}
|
||||
|
||||
void ActionCallback(ui::Button * sender) override
|
||||
{
|
||||
v->c->PopBindingByFunctionId(functionId);
|
||||
v->BuildKeyBindingsListView();
|
||||
v->c->NotifyBindingsChanged();
|
||||
}
|
||||
};
|
||||
|
||||
addButton->SetActionCallback(new AddBindingAction(this, binding.functionId, binding.description));
|
||||
|
||||
// only add in a remove button if we have a binding attached to the current function
|
||||
if (hasBinding)
|
||||
{
|
||||
ui::Button* removeButton = new ui::Button(ui::Point(removeButtonPosX, addRemoveButtonsPosY), ui::Point(20, 16), "-", "Remove a binding from this action");
|
||||
scrollPanel->AddChild(removeButton);
|
||||
removeButton->SetActionCallback(new RemoveBindingAction(this, binding.functionId));
|
||||
}
|
||||
}
|
||||
|
||||
scrollPanel->InnerSize = ui::Point(Size.X, currentY);
|
||||
scrollPanel->SetScrollPosition(scrollPos);
|
||||
}
|
||||
|
||||
void KeyboardBindingsView::OnKeyReleased()
|
||||
{
|
||||
for (auto textbox : textboxes)
|
||||
{
|
||||
if (textbox->GetText().length() == 0)
|
||||
{
|
||||
textbox->SetTextToPrevious();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardBindingsView::AttachController(KeyboardBindingsController* c_)
|
||||
{
|
||||
c = c_;
|
||||
}
|
||||
|
||||
void KeyboardBindingsView::OnDraw()
|
||||
{
|
||||
Graphics * g = GetGraphics();
|
||||
g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3);
|
||||
g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255);
|
||||
}
|
||||
|
||||
void KeyboardBindingsView::OnTryExit(ExitMethod method)
|
||||
{
|
||||
c->Exit();
|
||||
}
|
||||
|
||||
void KeyboardBindingsView::OnKeyCombinationChanged(bool hasConflict)
|
||||
{
|
||||
// disable OK button if there's a conflict
|
||||
if (hasConflict)
|
||||
{
|
||||
okayButton->Enabled = false;
|
||||
conflictLabel->Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
okayButton->Enabled = true;
|
||||
conflictLabel->Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardBindingsView::~KeyboardBindingsView()
|
||||
{
|
||||
|
||||
}
|
37
src/gui/options/keyboardbindings/KeyboardBindingsView.h
Normal file
37
src/gui/options/keyboardbindings/KeyboardBindingsView.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef KEYBOARDBINDINGSVIEW_H
|
||||
#define KEYBOARDBINDINGSVIEW_H
|
||||
|
||||
#include "gui/interface/Window.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class ScrollPanel;
|
||||
class Button;
|
||||
class Label;
|
||||
}
|
||||
|
||||
class KeyboardBindingsController;
|
||||
class KeyboardBindingsTextbox;
|
||||
|
||||
class KeyboardBindingsView: public ui::Window
|
||||
{
|
||||
ui::ScrollPanel* scrollPanel;
|
||||
KeyboardBindingsController* c;
|
||||
public:
|
||||
KeyboardBindingsView();
|
||||
void OnDraw() override;
|
||||
void OnTryExit(ExitMethod method) override;
|
||||
void AttachController(KeyboardBindingsController* controller);
|
||||
virtual ~KeyboardBindingsView();
|
||||
void OnKeyCombinationChanged(bool hasConflict);
|
||||
void BuildKeyBindingsListView();
|
||||
void OnKeyReleased();
|
||||
void ClearScrollPanel();
|
||||
|
||||
protected:
|
||||
ui::Button* okayButton;
|
||||
ui::Label* conflictLabel;
|
||||
std::vector<KeyboardBindingsTextbox*> textboxes;
|
||||
};
|
||||
|
||||
#endif /* KEYBOARDBINDINGSVIEW_H */
|
Loading…
x
Reference in New Issue
Block a user