Compare commits

...

4 Commits

Author SHA1 Message Date
Tamás Bálint Misius
9e3158b4dc
Introduce shortcut contexts 2019-10-12 23:37:53 +02:00
Tamás Bálint Misius
348ef06a1f
Move stuff around 2019-09-20 18:09:51 +02:00
Tamás Bálint Misius
a2adff46ce
Get the codebase to compile 2019-09-17 19:44:29 +02:00
Ian Bastos
bf0cc9ba5f
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
2019-09-17 19:44:37 +02:00
18 changed files with 1916 additions and 394 deletions

View File

@ -3,6 +3,8 @@
#include <vector>
#include <list>
#include <tuple>
#include <utility>
#include "common/String.h"
#include "common/Singleton.h"
@ -10,6 +12,77 @@
#include "User.h"
template<class Item>
void JsonArrayItemToTupleItem(Item &i, Json::Value const &v);
template<>
inline void JsonArrayItemToTupleItem<bool>(bool &i, Json::Value const &v)
{
i = v.asBool();
}
template<>
inline void JsonArrayItemToTupleItem<int>(int &i, Json::Value const &v)
{
i = v.asInt();
}
template<>
inline void JsonArrayItemToTupleItem<ByteString>(ByteString &i, Json::Value const &v)
{
auto s = v.asString();
i = ByteString(s.begin(), s.end());
}
template<std::size_t Item = 0, class... Types>
typename std::enable_if<Item == sizeof...(Types), void>::type
JsonArrayToTuple(std::tuple<Types...> &, Json::Value const &)
{
}
template<std::size_t Item = 0, class... Types>
typename std::enable_if<Item < sizeof...(Types), void>::type
JsonArrayToTuple(std::tuple<Types...> &t, Json::Value const &va)
{
JsonArrayItemToTupleItem(std::get<Item>(t), va[(Json::ArrayIndex)Item]);
JsonArrayToTuple<Item + 1, Types...>(t, va);
}
template<class Item>
void JsonArrayItemFromTupleItem(Item const &i, Json::Value &v);
template<>
inline void JsonArrayItemFromTupleItem<bool>(bool const &i, Json::Value &v)
{
v = Json::Value((bool)i);
}
template<>
inline void JsonArrayItemFromTupleItem<int>(int const &i, Json::Value &v)
{
v = Json::Value((Json::Int)i);
}
template<>
inline void JsonArrayItemFromTupleItem<ByteString>(ByteString const &i, Json::Value &v)
{
v = Json::Value(std::string(i.begin(), i.end()));
}
template<std::size_t Item = 0, class... Types>
typename std::enable_if<Item == sizeof...(Types), void>::type
JsonArrayFromTuple(std::tuple<Types...> const &, Json::Value &)
{
}
template<std::size_t Item = 0, class... Types>
typename std::enable_if<Item < sizeof...(Types), void>::type
JsonArrayFromTuple(std::tuple<Types...> const &t, Json::Value &va)
{
JsonArrayItemFromTupleItem(std::get<Item>(t), va[(Json::ArrayIndex)Item]);
JsonArrayFromTuple<Item + 1, Types...>(t, va);
}
class SaveInfo;
class SaveFile;
class SaveComment;
@ -185,6 +258,36 @@ public:
void SetPref(ByteString prop, Json::Value value);
void SetPref(ByteString property, std::vector<Json::Value> value);
void SetPrefUnicode(ByteString prop, String value);
template<class Tuple>
std::vector<Tuple> GetPrefTupleArray(ByteString prop)
{
try
{
Json::Value arr = GetPref(preferences, prop);
std::vector<Tuple> ret(arr.size());
for (int i = 0; i < (int)arr.size(); i++)
{
JsonArrayToTuple(ret[i], arr[i]);
}
return ret;
}
catch (std::exception &)
{
}
return std::vector<Tuple>();
}
template<class Tuple>
void SetPref(ByteString prop, std::vector<Tuple> const &value)
{
std::vector<Json::Value> arr(value.size());
for (int i = 0; i < (int)arr.size(); i++)
{
JsonArrayFromTuple(value[i], arr[i]);
}
SetPref(prop, arr);
}
};
#endif // CLIENT_H

View File

@ -661,7 +661,7 @@ bool GameController::MouseDown(int x, int y, unsigned button)
{
MouseDownEvent ev(x, y, button);
bool ret = commandInterface->HandleEvent(LuaEvents::mousedown, &ev);
if (ret && y<YRES && x<XRES && !gameView->GetPlacingSave() && !gameView->GetPlacingZoom())
if (ret && y<YRES && x<XRES && gameView->IsIdle())
{
ui::Point point = gameModel->AdjustZoomCoords(ui::Point(x, y));
x = point.X;
@ -687,7 +687,7 @@ bool GameController::MouseUp(int x, int y, unsigned button, char type)
bool ret = commandInterface->HandleEvent(LuaEvents::mouseup, &ev);
if (type)
return ret;
if (ret && foundSignID != -1 && y<YRES && x<XRES && !gameView->GetPlacingSave())
if (ret && foundSignID != -1 && y<YRES && x<XRES && gameView->IsIdle())
{
ui::Point point = gameModel->AdjustZoomCoords(ui::Point(x, y));
x = point.X;
@ -752,7 +752,7 @@ bool GameController::KeyPress(int key, int scan, bool repeat, bool shift, bool c
if (ret)
{
Simulation * sim = gameModel->GetSimulation();
if (!gameView->GetPlacingSave())
if (gameView->IsIdle())
{
// Go right command
if (key == SDLK_RIGHT)

View File

@ -156,6 +156,8 @@ GameModel::GameModel():
mouseClickRequired = Client::Ref().GetPrefBool("MouseClickRequired", false);
includePressure = Client::Ref().GetPrefBool("Simulation.IncludePressure", true);
keyconfig = Client::Ref().GetPrefTupleArray<decltype(keyconfig)::value_type>("Keyconfig");
}
GameModel::~GameModel()
@ -188,6 +190,8 @@ GameModel::~GameModel()
Client::Ref().SetPref("MouseClickRequired", mouseClickRequired);
Client::Ref().SetPref("Simulation.IncludePressure", includePressure);
Client::Ref().SetPref("Keyconfig", keyconfig);
Favorite::Ref().SaveFavoritesToPrefs();
for (size_t i = 0; i < menuList.size(); i++)
@ -546,6 +550,7 @@ void GameModel::AddObserver(GameView * observer){
observer->NotifyColourActivePresetChanged(this);
observer->NotifyQuickOptionsChanged(this);
observer->NotifyLastToolChanged(this);
observer->NotifyKeyconfigChanged(this);
UpdateQuickOptions();
}
@ -691,6 +696,14 @@ void GameModel::SetSave(SaveInfo * newSave, bool invertIncludePressure)
UpdateQuickOptions();
}
void GameModel::notifyKeyconfigChanged()
{
for (auto observer : observers)
{
observer->NotifyKeyconfigChanged(this);
}
}
SaveFile * GameModel::GetSaveFile()
{
return currentFile;
@ -1320,3 +1333,14 @@ void GameModel::SetIncludePressure(bool includePressure_)
{
includePressure = includePressure_;
}
Keyconfig GameModel::GetKeyconfig()
{
return keyconfig;
}
void GameModel::SetKeyconfig(Keyconfig keyconfig_)
{
keyconfig = keyconfig_;
notifyKeyconfigChanged();
}

View File

@ -7,6 +7,7 @@
#include "gui/interface/Colour.h"
#include "client/User.h"
#include "gui/interface/Point.h"
#include "Keyconfig.h"
class Menu;
class Tool;
@ -103,6 +104,10 @@ private:
void notifyToolTipChanged();
void notifyQuickOptionsChanged();
void notifyLastToolChanged();
void notifyKeyconfigChanged();
Keyconfig keyconfig;
public:
GameModel();
~GameModel();
@ -159,6 +164,9 @@ public:
int GetBrushID();
void SetBrushID(int i);
Keyconfig GetKeyconfig();
void SetKeyconfig(Keyconfig keyconfig);
void SetVote(int direction);
SaveInfo * GetSave();
SaveFile * GetSaveFile();

View File

@ -36,6 +36,10 @@
#include "simulation/SimulationData.h"
#include "simulation/ElementDefs.h"
#include "ElementClasses.h"
// #include "gui/keyconfig/KeyconfigMap.h"
#include <algorithm>
#include <cassert>
#ifdef GetUserName
# undef GetUserName // dammit windows
@ -210,7 +214,6 @@ GameView::GameView():
drawMode(DrawPoints),
drawPoint1(0, 0),
drawPoint2(0, 0),
selectMode(SelectNone),
selectPoint1(0, 0),
selectPoint2(0, 0),
currentMouse(0, 0),
@ -218,7 +221,6 @@ GameView::GameView():
placeSaveThumb(NULL),
placeSaveOffset(0, 0)
{
int currentX = 1;
//Set up UI
class SearchAction : public ui::ButtonAction
@ -462,6 +464,250 @@ GameView::GameView():
};
colourPicker = new ui::Button(ui::Point((XRES/2)-8, YRES+1), ui::Point(16, 16), "", "Pick Colour");
colourPicker->SetActionCallback(new ColourPickerAction(this));
AddFunction("DEFAULT_FUN_TOGGLE_CONSOLE", "Toggle Console", [this]() {
SDL_StopTextInput();
SDL_StartTextInput();
c->ShowConsole();
});
AddFunction("DEFAULT_FUN_PAUSE_SIMULATION", "Pause Simulation", [this]() {
c->SetPaused();
});
AddFunction("DEFAULT_FUN_UNDO", "Undo", [this]() {
if (!isMouseDown)
{
c->HistoryRestore();
}
});
AddFunction("DEFAULT_FUN_REDO", "Redo", [this]() {
if (!isMouseDown)
{
c->HistoryForward();
}
});
AddFunction("DEFAULT_FUN_ENABLE_ZOOM", "Enable Zoom", [this]() {
// sticky = altBehaviour;
isMouseDown = false;
zoomCursorFixed = false;
c->SetZoomEnabled(true);
}, [this]() {
if (!zoomCursorFixed)
{
c->SetZoomEnabled(false);
}
});
AddFunction("DEFAULT_FUN_PROPERTY_TOOL", "Switch to Property Tool", [this]() {
c->SetActiveTool(1, "DEFAULT_UI_PROPERTY");
});
AddFunction("DEFAULT_FUN_TOGGLE_DEBUG_HUD", "Toggle Debug HUD", [this]() {
SetDebugHUD(!GetDebugHUD());
});
AddFunction("DEFAULT_FUN_RELOAD_SIMULATION", "Reload Simulation", [this]() {
c->ReloadSim();
});
AddFunction("DEFAULT_FUN_SAVE_AUTHORSHIP_INFO", "Show Authorship Info", [this]() {
switch (Client::Ref().GetAuthUser().UserElevation)
{
case User::ElevationModerator:
case User::ElevationAdmin:
new InformationMessage("Save authorship info", ByteString(Client::Ref().GetAuthorInfo().toStyledString()).FromUtf8(), true);
break;
default:
break;
}
});
AddFunction("DEFAULT_FUN_OPEN_ELEMENT_SEARCH", "Open Element Search", [this]() {
c->OpenElementSearch();
});
AddFunction("DEFAULT_FUN_FIND_MODE", "Toggle Find Mode", [this]() {
Tool *active = c->GetActiveTool(0);
if (!active->GetIdentifier().Contains("_PT_") || (ren->findingElement == active->GetToolID()))
{
ren->findingElement = 0;
}
else
{
ren->findingElement = active->GetToolID();
}
});
AddFunction("DEFAULT_FUN_FRAME_STEP", "Next Frame", [this]() {
c->FrameStep();
});
AddFunction("DEFAULT_FUN_SHOW_GRAVITY_GRID", "Toggle Gravity Grid", [this]() {
c->ShowGravityGrid();
});
AddFunction("DEFAULT_FUN_DECREASE_GRAVITY_GRID_SIZE", "Decrease Gravity Grid Size", [this]() {
c->AdjustGridSize(-1);
});
AddFunction("DEFAULT_FUN_INCREASE_GRAVITY_GRID_SIZE", "Increase Gravity Grid Size", [this]() {
c->AdjustGridSize(1);
});
AddFunction("DEFAULT_FUN_TOGGLE_INTRO_TEXT", "Toggle Intro Text", [this]() {
if (!introText)
{
introText = 8047;
}
else
{
introText = 0;
}
});
AddFunction("DEFAULT_FUN_TOGGLE_HUD", "Toggle HUD", [this]() {
showHud = !showHud;
});
AddFunction("DEFAULT_FUN_TOGGLE_DECORATIONS_LAYER", "Toggle Decorations Layer", [this]() {
c->SetDecoration();
});
AddFunction("DEFAULT_FUN_TOGGLE_DECORATION_TOOL", "Toggle Decoration Tool", [this]() {
if (colourPicker->GetParentWindow())
{
c->SetActiveMenu(lastMenu);
}
else
{
c->SetDecoration(true);
c->SetPaused(true);
c->SetActiveMenu(SC_DECO);
}
});
AddFunction("DEFAULT_FUN_TOGGLE_AIR_MODE", "Cycle Air Mode", [this]() {
c->SwitchAir();
});
AddFunction("DEFAULT_FUN_QUIT", "Quit", [this]() {
ui::Engine::Ref().ConfirmExit();
});
AddFunction("DEFAULT_FUN_TOGGLE_HEAT", "Toggle Ambient Heat", [this]() {
c->ToggleAHeat();
});
AddFunction("DEFAULT_FUN_TOGGLE_NEWTONIAN_GRAVITY", "Toggle Newtonian Gravity", [this]() {
c->ToggleNewtonianGravity();
});
AddFunction("DEFAULT_FUN_RESET_SPARK", "Reset Sparks", [this]() {
c->ResetSpark();
});
AddFunction("DEFAULT_FUN_RESET_AIR", "Reset Air", [this]() {
c->ResetAir();
});
AddFunction("DEFAULT_FUN_COPY", "Copy", [this]() {
PushContext("DEFAULT_CTX_EDIT_COPY");
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;
});
AddFunction("DEFAULT_FUN_CUT", "Cut", [this]() {
PushContext("DEFAULT_CTX_EDIT_CUT");
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;
});
AddFunction("DEFAULT_FUN_PASTE", "Paste", [this]() {
if (c->LoadClipboard())
{
selectPoint1 = selectPoint2 = mousePosition;
isMouseDown = false;
}
});
AddFunction("DEFAULT_FUN_STAMP_TOOL", "Stamp", [this]() {
std::vector<ByteString> stampList = Client::Ref().GetStamps(0, 1);
if (stampList.size())
{
SaveFile *saveFile = Client::Ref().GetStamp(stampList[0]);
if (saveFile && saveFile->GetGameSave())
{
c->LoadStamp(saveFile->GetGameSave());
delete saveFile;
selectPoint1 = selectPoint2 = mousePosition;
isMouseDown = false;
}
}
});
AddFunction("DEFAULT_FUN_OPEN_STAMPS", "Open Stamp Browser", [this]() {
// selectMode = SelectNone;
selectPoint1 = selectPoint2 = ui::Point(-1, -1);
c->OpenStamps();
});
AddFunction("DEFAULT_FUN_INCREASE_BRUSH_ZOOM_SIZE", "Increase Brush or Zoom Window Size", [this]() {
if (zoomEnabled && !zoomCursorFixed)
{
c->AdjustZoomSize(1, !altBehaviour);
}
else
{
c->AdjustBrushSize(1, !altBehaviour, shiftBehaviour, ctrlBehaviour);
}
});
AddFunction("DEFAULT_FUN_DECREASE_BRUSH_ZOOM_SIZE", "Decrease Brush or Zoom Window Size", [this]() {
if (zoomEnabled && !zoomCursorFixed)
{
c->AdjustZoomSize(-1, !altBehaviour);
}
else
{
c->AdjustBrushSize(-1, !altBehaviour, shiftBehaviour, ctrlBehaviour);
}
});
AddFunction("DEFAULT_FUN_INSTALL_GAME", "Install Game", [this]() {
c->Install();
});
AddFunction("DEFAULT_FUN_INVERT_AIR_SIMULATION", "Reverse Air Vectors", [this]() {
c->InvertAirSim();
});
AddFunction("DEFAULT_FUN_TOGGLE_REPLACE_MODE", "Toggle Replace Mode", [this]() {
c->SetReplaceModeFlags(c->GetReplaceModeFlags() ^ SPECIFIC_DELETE);
});
AddFunction("DEFAULT_FUN_TOGGLE_SPECIFIC_DELETE_MODE", "Toggle Specific Delete Mode", [this]() {
c->SetReplaceModeFlags(c->GetReplaceModeFlags() ^ REPLACE_MODE);
});
AddFunction("DEFAULT_FUN_CYCLE_BRUSH", "Cycle Brush", [this]() {
c->ChangeBrush();
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_ALTERNATIVEVELOCITY", "Alternative Velocity Display", [this]() {
c->LoadRenderPreset(0);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_VELOCITY", "Velocity Display", [this]() {
c->LoadRenderPreset(1);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_PRESSURE", "Pressure Display", [this]() {
c->LoadRenderPreset(2);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_PERSISTENT", "Persistent Display", [this]() {
c->LoadRenderPreset(3);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_FIRE", "Fire Display", [this]() {
c->LoadRenderPreset(4);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_BLOB", "Blob Display", [this]() {
c->LoadRenderPreset(5);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_HEAT", "Heat Display", [this]() {
c->LoadRenderPreset(6);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_FANCY", "Fancy Display", [this]() {
c->LoadRenderPreset(7);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_NOTHING", "Nothing Display", [this]() {
c->LoadRenderPreset(8);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_HEATGRADIENT", "Heat Gradient Display", [this]() {
c->LoadRenderPreset(9);
});
AddFunction("DEFAULT_FUN_RENDER_PRESET_LIFEGRADIENT", "Life Gradient Display", [this]() {
if (showDebug)
{
c->LoadRenderPreset(10);
}
});
AddContext("DEFAULT_CTX_IDLE", "When editing");
AddContext("DEFAULT_CTX_EDIT_COPY", "When copying");
AddContext("DEFAULT_CTX_EDIT_CUT", "When cutting");
AddContext("DEFAULT_CTX_EDIT_PASTE", "When pasting");
AddContext("DEFAULT_CTX_EDIT_STAMP", "When creating a stamp");
PushContext("DEFAULT_CTX_IDLE");
}
GameView::~GameView()
@ -589,6 +835,11 @@ public:
}
};
void GameView::NotifyKeyconfigChanged(GameModel * sender)
{
keyconfig = sender->GetKeyconfig();
}
void GameView::NotifyQuickOptionsChanged(GameModel * sender)
{
for (size_t i = 0; i < quickOptionButtons.size(); i++)
@ -681,16 +932,6 @@ ui::Point GameView::GetMousePosition()
return currentMouse;
}
bool GameView::GetPlacingSave()
{
return selectMode != SelectNone;
}
bool GameView::GetPlacingZoom()
{
return zoomEnabled && !zoomCursorFixed;
}
void GameView::NotifyActiveToolsChanged(GameModel * sender)
{
decoBrush = false;
@ -1145,14 +1386,21 @@ void GameView::OnMouseMove(int x, int y, int dx, int dy)
bool newMouseInZoom = c->MouseInZoom(ui::Point(x, y));
mousePosition = c->PointTranslate(ui::Point(x, y));
currentMouse = ui::Point(x, y);
if (selectMode != SelectNone)
if (GetContext().BeginsWith("DEFAULT_CTX_EDIT_"))
{
if (GetContext() == "DEFAULT_CTX_EDIT_PASTE")
{
if (selectMode == PlaceSave)
selectPoint1 = c->PointTranslate(ui::Point(x, y));
}
if (selectPoint1.X != -1)
{
selectPoint2 = c->PointTranslate(ui::Point(x, y));
}
else if (isMouseDown)
}
else
{
if (isMouseDown)
{
if (newMouseInZoom == mouseInZoom)
{
@ -1176,6 +1424,7 @@ void GameView::OnMouseMove(int x, int y, int dx, int dy)
c->MouseUp(x, y, 0, 2);
}
}
}
mouseInZoom = newMouseInZoom;
// set active menu (delayed)
@ -1195,7 +1444,7 @@ void GameView::OnMouseDown(int x, int y, unsigned button)
button = SDL_BUTTON_MIDDLE;
if (!(zoomEnabled && !zoomCursorFixed))
{
if (selectMode != SelectNone)
if (GetContext().BeginsWith("DEFAULT_CTX_EDIT_"))
{
isMouseDown = true;
if (button == SDL_BUTTON_LEFT && selectPoint1.X == -1)
@ -1251,11 +1500,16 @@ void GameView::OnMouseUp(int x, int y, unsigned button)
else if (isMouseDown)
{
isMouseDown = false;
if (selectMode != SelectNone)
if (GetContext().BeginsWith("DEFAULT_CTX_EDIT_"))
{
if (button == SDL_BUTTON_LEFT && selectPoint1.X != -1 && selectPoint1.Y != -1 && selectPoint2.X != -1 && selectPoint2.Y != -1)
{
if (selectMode == PlaceSave)
int x2 = (selectPoint1.X>selectPoint2.X) ? selectPoint1.X : selectPoint2.X;
int y2 = (selectPoint1.Y>selectPoint2.Y) ? selectPoint1.Y : selectPoint2.Y;
int x1 = (selectPoint2.X<selectPoint1.X) ? selectPoint2.X : selectPoint1.X;
int y1 = (selectPoint2.Y<selectPoint1.Y) ? selectPoint2.Y : selectPoint1.Y;
if (GetContext().EndsWith("PASTE"))
{
if (placeSaveThumb && y <= WINDOWH-BARSIZE)
{
@ -1275,22 +1529,21 @@ void GameView::OnMouseUp(int x, int y, unsigned button)
c->PlaceSave(ui::Point(thumbX, thumbY));
}
}
else
else if (GetContext().EndsWith("COPY"))
{
int x2 = (selectPoint1.X>selectPoint2.X) ? selectPoint1.X : selectPoint2.X;
int y2 = (selectPoint1.Y>selectPoint2.Y) ? selectPoint1.Y : selectPoint2.Y;
int x1 = (selectPoint2.X<selectPoint1.X) ? selectPoint2.X : selectPoint1.X;
int y1 = (selectPoint2.Y<selectPoint1.Y) ? selectPoint2.Y : selectPoint1.Y;
if (selectMode ==SelectCopy)
c->CopyRegion(ui::Point(x1, y1), ui::Point(x2, y2));
else if (selectMode == SelectCut)
}
else if (GetContext().EndsWith("CUT"))
{
c->CutRegion(ui::Point(x1, y1), ui::Point(x2, y2));
else if (selectMode == SelectStamp)
}
else if (GetContext().EndsWith("STAMP"))
{
c->StampRegion(ui::Point(x1, y1), ui::Point(x2, y2));
}
}
selectMode = SelectNone;
return;
PopContext();
}
ui::Point finalDrawPoint2 = c->PointTranslate(currentMouse);
@ -1327,9 +1580,11 @@ void GameView::OnMouseUp(int x, int y, unsigned button)
c->DrawFill(toolIndex, finalDrawPoint2);
}
}
else if (button != SDL_BUTTON_LEFT && GetContext().BeginsWith("DEFAULT_CTX_EDIT_"))
{
// this shouldn't happen, but do this just in case
else if (selectMode != SelectNone && button != SDL_BUTTON_LEFT)
selectMode = SelectNone;
PopContext();
}
// update the drawing mode for the next line
// since ctrl/shift state may have changed since we started drawing
@ -1341,7 +1596,7 @@ void GameView::ToolTip(ui::Point senderPosition, String toolTip)
// buttom button tooltips
if (senderPosition.Y > Size.Y-17)
{
if (selectMode == PlaceSave || selectMode == SelectNone)
if (GetContext() == "DEFAULT_CTX_IDLE" || GetContext() == "DEFAULT_CTX_EDIT_PASTE")
{
buttonTip = toolTip;
isButtonTipFadingIn = true;
@ -1369,7 +1624,7 @@ void GameView::OnMouseWheel(int x, int y, int d)
{
if (!d)
return;
if (selectMode != SelectNone)
if (GetContext() != "DEFAULT_CTX_IDLE")
{
return;
}
@ -1385,316 +1640,13 @@ void GameView::OnMouseWheel(int x, int y, int d)
void GameView::BeginStampSelection()
{
selectMode = SelectStamp;
PushContext("DEFAULT_CTX_EDIT_STAMP");
selectPoint1 = selectPoint2 = ui::Point(-1, -1);
isMouseDown = false;
buttonTip = "\x0F\xEF\xEF\020Click-and-drag to specify an area to create a stamp (right click = cancel)";
buttonTipShow = 120;
}
void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
{
if (introText > 50)
{
introText = 50;
}
if (selectMode != SelectNone)
{
if (selectMode == PlaceSave)
{
switch (key)
{
case SDLK_RIGHT:
c->TranslateSave(ui::Point(1, 0));
return;
case SDLK_LEFT:
c->TranslateSave(ui::Point(-1, 0));
return;
case SDLK_UP:
c->TranslateSave(ui::Point(0, -1));
return;
case SDLK_DOWN:
c->TranslateSave(ui::Point(0, 1));
return;
}
if (scan == SDL_SCANCODE_R && !repeat)
{
if (ctrl && shift)
{
//Vertical flip
c->TransformSave(m2d_new(1,0,0,-1));
}
else if (!ctrl && shift)
{
//Horizontal flip
c->TransformSave(m2d_new(-1,0,0,1));
}
else
{
//Rotate 90deg
c->TransformSave(m2d_new(0,1,-1,0));
}
return;
}
}
}
if (repeat)
return;
bool didKeyShortcut = true;
switch(scan)
{
case SDL_SCANCODE_GRAVE:
SDL_StopTextInput();
SDL_StartTextInput();
c->ShowConsole();
break;
case SDL_SCANCODE_SPACE: //Space
c->SetPaused();
break;
case SDL_SCANCODE_Z:
if (selectMode != SelectNone && isMouseDown)
break;
if (ctrl && !isMouseDown)
{
if (shift)
c->HistoryForward();
else
c->HistoryRestore();
}
else
{
isMouseDown = false;
zoomCursorFixed = false;
c->SetZoomEnabled(true);
}
break;
case SDL_SCANCODE_P:
case SDL_SCANCODE_F2:
if (ctrl)
{
if (shift)
c->SetActiveTool(1, "DEFAULT_UI_PROPERTY");
else
c->SetActiveTool(0, "DEFAULT_UI_PROPERTY");
}
else
screenshot();
break;
case SDL_SCANCODE_F3:
SetDebugHUD(!GetDebugHUD());
break;
case SDL_SCANCODE_F5:
c->ReloadSim();
break;
case SDL_SCANCODE_A:
if ((Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator
|| Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin) && ctrl)
{
ByteString authorString = Client::Ref().GetAuthorInfo().toStyledString();
new InformationMessage("Save authorship info", authorString.FromUtf8(), true);
}
break;
case SDL_SCANCODE_R:
if (ctrl)
c->ReloadSim();
break;
case SDL_SCANCODE_E:
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();
}
else
c->FrameStep();
break;
case SDL_SCANCODE_G:
if (ctrl)
c->ShowGravityGrid();
else if(shift)
c->AdjustGridSize(-1);
else
c->AdjustGridSize(1);
break;
case SDL_SCANCODE_F1:
if(!introText)
introText = 8047;
else
introText = 0;
break;
case SDL_SCANCODE_H:
if(ctrl)
{
if(!introText)
introText = 8047;
else
introText = 0;
}
else
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);
}
break;
case SDL_SCANCODE_Y:
if (ctrl)
{
c->HistoryForward();
}
else
{
c->SwitchAir();
}
break;
case SDL_SCANCODE_ESCAPE:
case SDL_SCANCODE_Q:
ui::Engine::Ref().ConfirmExit();
break;
case SDL_SCANCODE_U:
c->ToggleAHeat();
break;
case SDL_SCANCODE_N:
c->ToggleNewtonianGravity();
break;
case SDL_SCANCODE_EQUALS:
if(ctrl)
c->ResetSpark();
else
c->ResetAir();
break;
case SDL_SCANCODE_C:
if(ctrl)
{
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 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:
{
std::vector<ByteString> stampList = Client::Ref().GetStamps(0, 1);
if (stampList.size())
{
SaveFile *saveFile = Client::Ref().GetStamp(stampList[0]);
if (!saveFile || !saveFile->GetGameSave())
break;
c->LoadStamp(saveFile->GetGameSave());
delete saveFile;
selectPoint1 = selectPoint2 = mousePosition;
isMouseDown = false;
break;
}
}
case SDL_SCANCODE_K:
selectMode = SelectNone;
selectPoint1 = selectPoint2 = ui::Point(-1, -1);
c->OpenStamps();
break;
case SDL_SCANCODE_RIGHTBRACKET:
if(zoomEnabled && !zoomCursorFixed)
c->AdjustZoomSize(1, !alt);
else
c->AdjustBrushSize(1, !alt, shiftBehaviour, ctrlBehaviour);
break;
case SDL_SCANCODE_LEFTBRACKET:
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();
break;
case SDL_SCANCODE_SEMICOLON:
if (ctrl)
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^SPECIFIC_DELETE);
else
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^REPLACE_MODE);
break;
default:
didKeyShortcut = false;
}
if (!didKeyShortcut)
{
switch (key)
{
case SDLK_TAB: //Tab
c->ChangeBrush();
break;
case SDLK_INSERT:
if (ctrl)
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^SPECIFIC_DELETE);
else
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^REPLACE_MODE);
break;
case SDLK_DELETE:
c->SetReplaceModeFlags(c->GetReplaceModeFlags()^SPECIFIC_DELETE);
break;
}
}
if (shift && showDebug && key == '1')
c->LoadRenderPreset(10);
else if (key >= '0' && key <= '9')
{
c->LoadRenderPreset(key-'0');
}
}
void GameView::OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
{
if (repeat)
return;
if (scan == SDL_SCANCODE_Z)
{
if (!zoomCursorFixed && !alt)
c->SetZoomEnabled(false);
return;
}
}
void GameView::OnBlur()
{
disableAltBehaviour();
@ -1730,8 +1682,10 @@ void GameView::OnFileDrop(ByteString filename)
void GameView::OnTick(float dt)
{
if (selectMode == PlaceSave && !placeSaveThumb)
selectMode = SelectNone;
if (GetContext() == "DEFAULT_CTX_EDIT_PASTE" && !placeSaveThumb)
{
PopContext();
}
if (zoomEnabled && !zoomCursorFixed)
c->SetZoomPosition(currentMouse);
@ -1739,7 +1693,7 @@ void GameView::OnTick(float dt)
{
skipDraw = false;
}
else if (selectMode == SelectNone && isMouseDown)
else if (GetContext() == "DEFAULT_CTX_IDLE" && isMouseDown)
{
if (drawMode == DrawPoints)
{
@ -1798,7 +1752,9 @@ void GameView::OnTick(float dt)
if(infoTipPresence<0)
infoTipPresence = 0;
}
if (isButtonTipFadingIn || (selectMode != PlaceSave && selectMode != SelectNone))
if (isButtonTipFadingIn)
{
if (GetContext().BeginsWith("DEFAULT_CTX_EDIT_") && GetContext() != "DEFAULT_CTX_EDIT_PASTE")
{
isButtonTipFadingIn = false;
if(buttonTipShow < 120)
@ -1808,6 +1764,7 @@ void GameView::OnTick(float dt)
buttonTipShow = 120;
}
}
}
else if(buttonTipShow>0)
{
buttonTipShow -= int(dt)>0?int(dt):1;
@ -1902,6 +1859,90 @@ void GameView::DoDraw()
c->Tick();
}
void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
{
if (repeat)
{
return;
}
if (introText > 50)
{
introText = 50;
}
if (GetContext() == "DEFAULT_CTX_EDIT_PASTE")
{
switch (key)
{
case SDLK_RIGHT:
c->TranslateSave(ui::Point(1, 0));
return;
case SDLK_LEFT:
c->TranslateSave(ui::Point(-1, 0));
return;
case SDLK_UP:
c->TranslateSave(ui::Point(0, -1));
return;
case SDLK_DOWN:
c->TranslateSave(ui::Point(0, 1));
return;
}
if (scan == SDL_SCANCODE_R && !repeat)
{
if (ctrl && shift)
{
//Vertical flip
c->TransformSave(m2d_new(1,0,0,-1));
}
else if (!ctrl && shift)
{
//Horizontal flip
c->TransformSave(m2d_new(-1,0,0,1));
}
else
{
//Rotate 90deg
c->TransformSave(m2d_new(0,1,-1,0));
}
return;
}
}
// linear search for now, will figure out something smarter if we ever need to
auto current_context = GetContext();
auto shortcut = std::find_if(keyconfig.begin(), keyconfig.end(), [=](KeyconfigItem const &item) {
return std::get<0>(item) == current_context
&& std::get<1>(item) == ctrl
&& std::get<2>(item) == shift
&& std::get<3>(item) == alt
&& std::get<4>(item) == scan;
});
if (shortcut != keyconfig.end())
{
auto func = std::get<5>(*shortcut);
active_view_functions.push_back({ func, scan });
view_functions[func].on();
}
}
void GameView::OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
{
if (repeat)
{
return;
}
auto active = std::find_if(active_view_functions.begin(), active_view_functions.end(), [=](ActiveViewFunction const &func) {
return func.scan != scan;
});
if (active != active_view_functions.end())
{
view_functions[active->func].off();
active_view_functions.erase(active);
}
}
void GameView::NotifyNotificationsChanged(GameModel * sender)
{
class NotificationButtonAction : public ui::ButtonAction
@ -1982,20 +2023,29 @@ void GameView::NotifyLogChanged(GameModel * sender, String entry)
}
void GameView::NotifyPlaceSaveChanged(GameModel * sender)
{
if (placeSaveThumb)
{
delete placeSaveThumb;
}
placeSaveOffset = ui::Point(0, 0);
if(sender->GetPlaceSave())
{
SaveRenderer::Ref().CopyModes(sender->GetRenderer());
if (!placeSaveThumb) // we weren't in DEFAULT_CTX_EDIT_PASTE
{
PushContext("DEFAULT_CTX_EDIT_PASTE");
}
placeSaveThumb = SaveRenderer::Ref().Render(sender->GetPlaceSave());
selectMode = PlaceSave;
selectPoint2 = mousePosition;
}
else
{
if (placeSaveThumb)
{
PopContext(); // we were in DEFAULT_CTX_EDIT_PASTE
}
placeSaveThumb = NULL;
selectMode = SelectNone;
}
}
@ -2004,7 +2054,7 @@ void GameView::enableShiftBehaviour()
if (!shiftBehaviour)
{
shiftBehaviour = true;
if (!isMouseDown || selectMode != SelectNone)
if (!isMouseDown || GetContext() != "DEFAULT_CTX_IDLE")
UpdateDrawMode();
UpdateToolStrength();
}
@ -2015,7 +2065,7 @@ void GameView::disableShiftBehaviour()
if (shiftBehaviour)
{
shiftBehaviour = false;
if (!isMouseDown || selectMode != SelectNone)
if (!isMouseDown || GetContext() != "DEFAULT_CTX_IDLE")
UpdateDrawMode();
UpdateToolStrength();
}
@ -2044,7 +2094,7 @@ void GameView::enableCtrlBehaviour()
if (!ctrlBehaviour)
{
ctrlBehaviour = true;
if (!isMouseDown || selectMode != SelectNone)
if (!isMouseDown || GetContext() != "DEFAULT_CTX_IDLE")
UpdateDrawMode();
UpdateToolStrength();
@ -2069,7 +2119,7 @@ void GameView::disableCtrlBehaviour()
if (ctrlBehaviour)
{
ctrlBehaviour = false;
if (!isMouseDown || selectMode != SelectNone)
if (!isMouseDown || GetContext() != "DEFAULT_CTX_IDLE")
UpdateDrawMode();
UpdateToolStrength();
@ -2135,7 +2185,7 @@ void GameView::OnDraw()
ren->clearScreen(1.0f);
ren->RenderBegin();
ren->SetSample(c->PointTranslate(currentMouse).X, c->PointTranslate(currentMouse).Y);
if (selectMode == SelectNone && (!zoomEnabled || zoomCursorFixed) && activeBrush && (isMouseDown || (currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES)))
if (GetContext() == "DEFAULT_CTX_IDLE" && (!zoomEnabled || zoomCursorFixed) && activeBrush && (isMouseDown || (currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES)))
{
ui::Point finalCurrentMouse = c->PointTranslate(currentMouse);
ui::Point initialDrawPoint = drawPoint1;
@ -2197,9 +2247,9 @@ void GameView::OnDraw()
}
}
if(selectMode!=SelectNone)
if (GetContext().BeginsWith("DEFAULT_CTX_EDIT_"))
{
if(selectMode==PlaceSave)
if (GetContext().EndsWith("PASTE"))
{
if(placeSaveThumb && selectPoint2.X!=-1)
{
@ -2556,3 +2606,45 @@ ui::Point GameView::rectSnapCoords(ui::Point point1, ui::Point point2)
// SW-NE
return point1 + ui::Point((diff.X - diff.Y)/2, (diff.Y - diff.X)/2);
}
void GameView::PushContext(ByteString new_context)
{
context.push(new_context);
}
void GameView::PopContext()
{
context.pop();
assert(context.size());
}
ByteString GameView::GetContext() const
{
return context.top();
}
bool GameView::IsIdle() const
{
return GetContext() == "DEFAULT_CTX_IDLE";
}
void GameView::AddFunction(ByteString name, ByteString description, ViewFunctionOn on, ViewFunctionOff off)
{
view_functions.insert(std::make_pair(name, ViewFunction{ on, off, description }));
}
void GameView::RemoveFunction(ByteString name)
{
view_functions.erase(name);
}
void GameView::AddContext(ByteString name, ByteString description)
{
view_contexts.insert(std::make_pair(name, ViewContext{ description }));
}
void GameView::RemoveContext(ByteString name)
{
view_contexts.erase(name);
}

View File

@ -3,20 +3,19 @@
#include <vector>
#include <deque>
#include <map>
#include <stack>
#include <functional>
#include "common/String.h"
#include "gui/interface/Window.h"
#include "simulation/Sample.h"
#include "Keyconfig.h"
enum DrawMode
{
DrawPoints, DrawLine, DrawRect, DrawFill
};
enum SelectMode
{
SelectNone, SelectStamp, SelectCopy, SelectCut, PlaceSave
};
namespace ui
{
class Button;
@ -32,6 +31,10 @@ class Brush;
class GameModel;
class GameView: public ui::Window
{
public:
using ViewFunctionOn = std::function<void ()>;
using ViewFunctionOff = std::function<void ()>;
private:
bool isMouseDown;
bool skipDraw;
@ -102,7 +105,7 @@ private:
ui::Point drawPoint1;
ui::Point drawPoint2;
SelectMode selectMode;
std::stack<ByteString> context;
ui::Point selectPoint1;
ui::Point selectPoint2;
@ -128,6 +131,30 @@ private:
void disableAltBehaviour();
void UpdateDrawMode();
void UpdateToolStrength();
Keyconfig keyconfig;
struct ViewContext
{
ByteString description;
};
std::map<ByteString, ViewContext> view_contexts;
struct ViewFunction
{
ViewFunctionOn on;
ViewFunctionOff off;
ByteString description;
};
std::map<ByteString, ViewFunction> view_functions;
struct ActiveViewFunction
{
ByteString func;
int scan;
};
std::vector<ActiveViewFunction> active_view_functions;
public:
GameView();
virtual ~GameView();
@ -139,17 +166,15 @@ public:
bool GetHudEnable();
void SetDebugHUD(bool mode);
bool GetDebugHUD();
bool GetPlacingSave();
bool GetPlacingZoom();
void SetActiveMenuDelayed(int activeMenu) { delayedActiveMenu = activeMenu; }
bool CtrlBehaviour(){ return ctrlBehaviour; }
bool ShiftBehaviour(){ return shiftBehaviour; }
bool AltBehaviour(){ return altBehaviour; }
SelectMode GetSelectMode() { return selectMode; }
void BeginStampSelection();
ui::Point GetPlaceSaveOffset() { return placeSaveOffset; }
void SetPlaceSaveOffset(ui::Point offset) { placeSaveOffset = offset; }
int Record(bool record);
bool IsIdle() const;
//all of these are only here for one debug lines
bool GetMouseDown() { return isMouseDown; }
@ -182,6 +207,7 @@ public:
void NotifyInfoTipChanged(GameModel * sender);
void NotifyQuickOptionsChanged(GameModel * sender);
void NotifyLastToolChanged(GameModel * sender);
void NotifyKeyconfigChanged(GameModel * sender);
void ToolTip(ui::Point senderPosition, String toolTip) override;
@ -212,6 +238,15 @@ public:
class ToolAction;
class OptionAction;
class OptionListener;
void PushContext(ByteString new_context);
void PopContext();
ByteString GetContext() const;
void AddFunction(ByteString name, ByteString description, ViewFunctionOn on = nullptr, ViewFunctionOff off = nullptr);
void RemoveFunction(ByteString name);
void AddContext(ByteString name, ByteString description);
void RemoveContext(ByteString name);
};
#endif // GAMEVIEW_H

18
src/gui/game/Keyconfig.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef KEYCONFIG_H_
#define KEYCONFIG_H_
#include <vector>
#include <tuple>
#include <string>
using KeyconfigItem = std::tuple<
ByteString, // context
bool, // ctrl
bool, // shift
bool, // alt
int, // scancode
ByteString // function
>;
using Keyconfig = std::vector<KeyconfigItem>;
#endif // KEYCONFIG_H_

View File

@ -0,0 +1,99 @@
static int Aa;
#if 0 // temporarily disabled so it doesn't interfere with what I'm doing -- LBPHacker
#include "KeyconfigController.h"
#include "KeyconfigView.h"
#include "Controller.h"
#include "client/Client.h"
#include "gui/options/OptionsController.h"
#include "KeyconfigModel.h"
KeyconfigController::KeyconfigController(OptionsController* _parent):
HasExited(false)
{
parent = _parent;
view = new KeyconfigView();
model = new KeyconfigModel();
model->AddObserver(view);
view->AttachController(this);
LoadBindingPrefs();
view->BuildKeyBindingsListView();
}
KeyconfigView* KeyconfigController::GetView()
{
return view;
}
void KeyconfigController::CreateModel(BindingModel _model)
{
model->CreateModel(_model);
}
void KeyconfigController::Save()
{
model->Save();
}
void KeyconfigController::ChangeModel(BindingModel _model)
{
model->RemoveModelByIndex(_model.index);
model->AddModel(_model);
}
void KeyconfigController::Exit()
{
view->CloseActiveWindow();
parent->NotifyKeyBindingsChanged();
HasExited = true;
}
void KeyconfigController::LoadBindingPrefs()
{
model->LoadBindingPrefs();
}
std::vector<BindingModel> KeyconfigController::GetBindingPrefs()
{
return model->GetBindingPrefs();
}
void KeyconfigController::NotifyBindingsChanged()
{
bool hasConflict = model->HasConflictingCombo();
model->NotifyBindingsChanged(hasConflict);
}
void KeyconfigController::ForceHasConflict()
{
view->OnKeyCombinationChanged(true);
}
void KeyconfigController::NotifyKeyReleased()
{
view->OnKeyReleased();
}
void KeyconfigController::PopBindingByFunctionId(int functionId)
{
model->PopBindingByFunctionId(functionId);
}
void KeyconfigController::ResetToDefaults()
{
model->WriteDefaultPrefs(true);
}
bool KeyconfigController::FunctionHasShortcut(int functionId)
{
return model->FunctionHasShortcut(functionId);
}
KeyconfigController::~KeyconfigController()
{
view->CloseActiveWindow();
delete view;
delete callback;
delete model;
}
#endif

View File

@ -0,0 +1,41 @@
#ifndef KEYCONFIGSCONTROLLER_H
#define KEYCONFIGSCONTROLLER_H
#include <vector>
class ControllerCallback;
class KeyconfigView;
class GameModel;
class OptionsController;
class KeyconfigModel;
struct BindingModel;
class KeyconfigController
{
ControllerCallback * callback;
KeyconfigView* view;
KeyconfigModel* model;
OptionsController* parent;
public:
bool HasExited;
KeyconfigController(OptionsController* _parent);
void Exit();
KeyconfigView * GetView();
virtual ~KeyconfigController();
void AddModel(BindingModel model);
void CreateModel(BindingModel model);
void ChangeModel(BindingModel model);
void Save();
void ForceHasConflict();
void NotifyKeyReleased();
void OnKeyReleased();
void NotifyBindingsChanged();
void PopBindingByFunctionId(int functionId);
bool FunctionHasShortcut(int functionId);
void ResetToDefaults();
void LoadBindingPrefs();
std::vector<BindingModel> GetBindingPrefs();
};
#endif /* KEYCONFIGSCONTROLLER_H */

View File

@ -0,0 +1,152 @@
#ifndef KEYCONFIGSMAP_H
#define KEYCONFIGSMAP_H
#include "common/String.h"
typedef struct KeyconfigMap
{
int id;
String description;
int functionId;
} KeyconfigMap;
typedef struct DefaultKeyconfigMap
{
ByteString keyCombo;
int bindingId; // KeyconfigMap id
} DefaultKeyconfigMap;
static KeyconfigMap 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 KeyconfigFunction
{
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 DefaultKeyconfigMap defaultKeyconfigMapArray[] =
{
{ "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

View File

@ -0,0 +1,364 @@
static int Aa;
#if 0 // temporarily disabled so it doesn't interfere with what I'm doing -- LBPHacker
#include "KeyconfigModel.h"
// #include "client/Client.h"
// #include "SDLCompat.h"
// #include "KeyconfigMap.h"
// #include <algorithm>
// #include "KeyconfigView.h"
// void KeyconfigModel::WriteDefaultFuncArray(bool force)
// {
// if (force)
// Client::Ref().ClearPref(ByteString(KEYCONFIG_FUNCS_PREF));
// for (auto defaultBinding : defaultKeyconfigMapArray)
// {
// int functionId;
// String description;
// for (auto functions : keyboardBindingFunctionMap)
// {
// if (functions.id == defaultBinding.bindingId)
// {
// functionId = functions.functionId;
// description = functions.description;
// }
// }
// ByteString pref = ByteString(KEYCONFIG_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 KeyconfigModel::WriteDefaultPrefs(bool force)
// {
// // Load temporary bindings into memory
// // this is so we can add in any new axctions
// // from the KeyconfigMap into our prefs
// LoadBindingPrefs();
// if (force)
// Client::Ref().ClearPref(ByteString(KEYCONFIG_PREF));
// WriteDefaultFuncArray(force);
// for (auto defaultBinding : defaultKeyconfigMapArray)
// {
// int functionId;
// String description;
// for (auto functions : keyboardBindingFunctionMap)
// {
// if (functions.id == defaultBinding.bindingId)
// {
// functionId = functions.functionId;
// description = functions.description;
// }
// }
// ByteString pref = ByteString(KEYCONFIG_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 KeyconfigModel::LoadBindingPrefs()
// {
// Json::Value bindings = Client::Ref().GetPrefJson(KEYCONFIG_PREF);
// bindingPrefs.clear();
// if (bindings != Json::nullValue)
// {
// Json::Value::Members keyComboJson = bindings.getMemberNames();
// int index = 0;
// for (auto& member : keyComboJson)
// {
// ByteString keyCombo(member);
// ByteString pref = ByteString(KEYCONFIG_PREF) + "." + keyCombo;
// Json::Value result = Client::Ref().GetPrefJson(pref);
// if (result != Json::nullValue)
// {
// BindingModel model;
// std::pair<int, int> 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<int, int>
// KeyconfigModel::GetModifierAndScanFromString(ByteString str)
// {
// int modifier = 0;
// int 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 KeyconfigModel::TurnOffFunctionShortcut(int functionId)
// {
// ByteString pref = ByteString(KEYCONFIG_FUNCS_PREF) + ByteString(".") + ByteString(functionId)
// + ByteString(".hasShortcut");
// Client::Ref().SetPref(pref, false);
// }
// void KeyconfigModel::TurnOnFunctionShortcut(int functionId)
// {
// ByteString pref = ByteString(KEYCONFIG_FUNCS_PREF) + ByteString(".") + ByteString(functionId)
// + ByteString(".hasShortcut");
// Client::Ref().SetPref(pref, true);
// }
// void KeyconfigModel::RemoveModelByIndex(int 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 KeyconfigModel::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 KeyconfigModel::AddModel(BindingModel model)
// {
// bindingPrefs.push_back(model);
// TurnOnFunctionShortcut(model.functionId);
// bool hasConflict = HasConflictingCombo();
// NotifyBindingsChanged(hasConflict);
// }
// bool KeyconfigModel::FunctionHasShortcut(int functionId)
// {
// ByteString pref = ByteString(KEYCONFIG_FUNCS_PREF) + ByteString(".") + ByteString(functionId)
// + ByteString(".hasShortcut");
// return Client::Ref().GetPrefBool(pref, false);
// }
// void KeyconfigModel::Save()
// {
// Client::Ref().ClearPref(KEYCONFIG_PREF);
// for (auto& binding : bindingPrefs)
// {
// ByteString mod(std::to_string(binding.modifier));
// ByteString scan(std::to_string(binding.scan));
// ByteString pref = ByteString(KEYCONFIG_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();
// }
int KeyconfigModel::GetFunctionForBinding(int scan, bool shift, bool ctrl, bool alt)
{
int 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 KeyconfigModel::PopBindingByFunctionId(int 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 KeyconfigModel::GetDisplayForModel(BindingModel model)
// {
// return model.description;
// }
// bool KeyconfigModel::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 KeyconfigModel::AddObserver(KeyconfigView* view)
{
observers.push_back(view);
view->NotifyKeyconfigChanged(this);
}
void KeyconfigModel::notifyKeyconfigChanged(bool hasConflict)
{
for (auto& observer : observers)
{
observer->NotifyKeyconfigChanged(hasConflict);
}
}
#endif

View File

@ -0,0 +1,81 @@
#ifndef KEYCONFIGSMODEL_H
#define KEYCONFIGSMODEL_H
// #include <vector>
// #include <utility>
// #include "common/String.h"
// #define KEYCONFIG_PREF "Keyconfig"
// #define KEYCONFIG_FUNCS_PREF "KeyconfigFunctions"
// #define BINDING_MASK 0x07
// #define BINDING_CTRL 0x01
// #define BINDING_ALT 0x02
// #define BINDING_SHIFT 0x04
// struct BindingModel
// {
// int modifier;
// int scan;
// int functionId;
// String description;
// int index;
// bool noShortcut;
// bool isNew;
// 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 KeyconfigView;
class KeyconfigModel
{
GameModel *gModel;
std::vector<KeyconfigView *> observers;
void notifyKeyconfigChanged();
public:
KeyconfigModel(GameModel *gModel);
void AddObserver(KeyconfigView* observer);
std::vector<BindingModel> GetBindingPrefs();
virtual ~KeyconfigModel();
// void RemoveModelByIndex(int index);
// void AddModel(BindingModel model);
// void CreateModel(BindingModel model);
// String GetDisplayForModel(BindingModel model);
// void NotifyBindingsChanged(bool hasConflict);
// bool HasConflictingCombo();
// void PopBindingByFunctionId(int functionId);
// void WriteDefaultFuncArray(bool force = false);
// bool FunctionHasShortcut(int functionId);
// int GetFunctionForBinding(int scan, bool shift, bool ctrl, bool alt);
// void Save();
// void WriteDefaultPrefs(bool force = false); // true if user clicks reset to defaults
// inline std::vector<BindingModel> GetBindingPrefs() const { return bindingPrefs; }
// void LoadBindingPrefs();
// protected:
// void TurnOffFunctionShortcut(int functionId);
// void TurnOnFunctionShortcut(int functionId);
// std::vector<BindingModel> bindingPrefs;
// std::pair<int, int> GetModifierAndScanFromString(ByteString str);
};
#endif // KEYCONFIGSMODEL_H

View File

@ -0,0 +1,122 @@
static int Aa;
#if 0 // temporarily disabled so it doesn't interfere with what I'm doing -- LBPHacker
#include "KeyconfigTextbox.h"
#include "SDLCompat.h"
#include "gui/interface/Window.h"
#include "client/Client.h"
#include "KeyconfigModel.h"
#include "KeyconfigController.h"
KeyconfigTextbox::KeyconfigTextbox(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 KeyconfigTextbox::OnMouseClick(int x, int y, unsigned button)
{
prevKey = GetText();
SetText("");
c->ForceHasConflict();
}
void KeyconfigTextbox::AttachController(KeyconfigController* _c)
{
c = _c;
}
void KeyconfigTextbox::SetModel(BindingModel _model)
{
model = _model;
}
void KeyconfigTextbox::SetTextToPrevious()
{
SetText(prevKey);
}
void KeyconfigTextbox::SetTextFromModifierAndScan(int modifier, int 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 KeyconfigTextbox::OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
{
ui::Textbox::OnKeyRelease(key, scan, repeat, shift, ctrl, alt);
int 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 = (int) 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();
}
#endif

View File

@ -0,0 +1,31 @@
#ifndef KEYCONFIGSTEXTBOX_H
#define KEYCONFIGSTEXTBOX_H
#include "gui/interface/Textbox.h"
#include "KeyconfigModel.h"
class KeyconfigController;
class KeyconfigTextbox: public ui::Textbox
{
public:
KeyconfigTextbox(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(int modifier, int scan);
void SetTextToPrevious();
void OnTextInput(String text) {}
void AttachController(KeyconfigController* _c);
protected:
String prevKey;
KeyconfigController* c;
BindingModel model;
};
#endif /* KEYCONFIGSTEXTBOX_H */

View File

@ -0,0 +1,293 @@
static int Aa;
#if 0 // temporarily disabled so it doesn't interfere with what I'm doing -- LBPHacker
#include "KeyconfigView.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 "KeyconfigMap.h"
#include "KeyconfigTextbox.h"
#include "KeyconfigModel.h"
#include "KeyconfigController.h"
#include "client/Client.h"
#include <vector>
#include <algorithm>
KeyconfigView::KeyconfigView() :
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:
KeyconfigView * v;
ResetDefaultsAction(KeyconfigView * 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:
KeyconfigView * v;
CloseAction(KeyconfigView * 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 KeyconfigView::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 KeyconfigView::BuildKeyBindingsListView()
{
int 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 < (int)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);
KeyconfigTextbox* 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 KeyconfigTextbox(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)
{
KeyconfigTextbox* tb = new KeyconfigTextbox(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:
KeyconfigView * v;
int functionId;
String desc;
AddBindingAction(KeyconfigView * v_, int _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:
KeyconfigView * v;
int functionId;
RemoveBindingAction(KeyconfigView * v_, int _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 KeyconfigView::OnKeyReleased()
{
for (auto textbox : textboxes)
{
if (textbox->GetText().length() == 0)
{
textbox->SetTextToPrevious();
}
}
}
void KeyconfigView::AttachController(KeyconfigController* c_)
{
c = c_;
}
void KeyconfigView::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 KeyconfigView::OnTryExit(ExitMethod method)
{
c->Exit();
}
void KeyconfigView::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;
}
}
KeyconfigView::~KeyconfigView()
{
}
#endif

View File

@ -0,0 +1,37 @@
#ifndef KEYCONFIGSVIEW_H
#define KEYCONFIGSVIEW_H
#include "gui/interface/Window.h"
namespace ui
{
class ScrollPanel;
class Button;
class Label;
}
class KeyconfigController;
class KeyconfigTextbox;
class KeyconfigView: public ui::Window
{
ui::ScrollPanel* scrollPanel;
KeyconfigController* c;
public:
KeyconfigView();
void OnDraw() override;
void OnTryExit(ExitMethod method) override;
void AttachController(KeyconfigController* controller);
virtual ~KeyconfigView();
void OnKeyCombinationChanged(bool hasConflict);
void BuildKeyBindingsListView();
void OnKeyReleased();
void ClearScrollPanel();
protected:
ui::Button* okayButton;
ui::Label* conflictLabel;
std::vector<KeyconfigTextbox*> textboxes;
};
#endif /* KEYCONFIGSVIEW_H */

View File

@ -111,7 +111,6 @@ void OptionsController::Exit()
HasExited = true;
}
OptionsController::~OptionsController()
{
view->CloseActiveWindow();

View File

@ -448,6 +448,29 @@ OptionsView::OptionsView():
tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
scrollPanel->AddChild(tempLabel);
currentY+=20;
class KeyconfigAction: public ui::ButtonAction
{
public:
KeyconfigAction() { }
void ActionCallback(ui::Button * sender) override
{
// OptionsView* v = (OptionsView*) sender->GetParentWindow();
// KeyconfigController* keyboardBindingsController = new KeyconfigController(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 KeyconfigAction());
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
{