Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/modelspinnermousebutton #5981

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions data/lang/ui-core/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,14 @@
"description": "",
"message": "Invert Mouse Y"
},
"NO_MIDDLE_MOUSE_BUTTON": {
"description": "",
"message": "No middle mouse button available"
},
"NO_MIDDLE_MOUSE_BUTTON_DESC": {
"description": "",
"message": "Use left mouse button to grab/spin objects when there is no middle mouse button available"
},
"IN_CARGO_HOLD": {
"description": "",
"message": "In cargo hold"
Expand Down
4 changes: 4 additions & 0 deletions data/pigui/modules/settings-window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -574,13 +574,17 @@ local function showControlsOptions()
ui.text(lui.CONTROL_OPTIONS)

local mouseYInvert = Input.GetMouseYInverted()
local middleMouseButton = Input.EmulateMiddleMouseButton()
local joystickEnabled = Input.GetJoystickEnabled()
binding_pages = Input.GetBindingPages()
local c

c,mouseYInvert = checkbox(lui.INVERT_MOUSE_Y, mouseYInvert)
if c then Input.SetMouseYInverted(mouseYInvert) end

c,middleMouseButton = checkbox(lui.NO_MIDDLE_MOUSE_BUTTON, middleMouseButton, lui.NO_MIDDLE_MOUSE_BUTTON_DESC)
if c then Input.SetMiddleMouseButton(middleMouseButton) end

c,joystickEnabled = checkbox(lui.ENABLE_JOYSTICK, joystickEnabled)
if c then Input.SetJoystickEnabled(joystickEnabled) end

Expand Down
4 changes: 3 additions & 1 deletion data/pigui/modules/system-view-ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,9 @@ local function displayOnScreenObjects()

-- click once: select or deselect a body
-- double click: zoom to body or reset viewpoint
local clicked = not ui.isAnyWindowHovered() and (ui.isMouseClicked(0) or ui.isMouseDoubleClicked(0))
local clicked = not ui.isAnyWindowHovered()
and not ui.ctrlHeld()
and (ui.isMouseClicked(0) or ui.isMouseDoubleClicked(0))
if clicked then
if hoveredObject then
selectedObject = hoveredObject.ref
Expand Down
1 change: 1 addition & 0 deletions src/GameConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ GameConfig::GameConfig(const map_string &override_)
map["SfxVolume"] = "0.8";
map["EnableJoystick"] = "1";
map["InvertMouseY"] = "0";
map["noMiddleMouseButton"] = "0";
map["FOVVertical"] = "65";
map["DisplayNavTunnel"] = "0";
map["CompactRadar"] = "1";
Expand Down
32 changes: 31 additions & 1 deletion src/Input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,13 @@ Manager::Manager(IniConfig *config, SDL_Window *window) :
m_capturingMouse(false),
joystickEnabled(true),
mouseYInvert(false),
noMiddleMouseButton(false), // Which means a middle mouse button is expected by default
m_enableBindings(true),
m_frameListChanged(false)
{
joystickEnabled = (m_config->Int("EnableJoystick")) ? true : false;
mouseYInvert = (m_config->Int("InvertMouseY")) ? true : false;
noMiddleMouseButton = (m_config->Int("noMiddleMouseButton")) ? true : false;

Input::InitJoysticks(m_config);
}
Expand Down Expand Up @@ -517,6 +519,15 @@ void Manager::SetMouseYInvert(bool state)
}
}

void Manager::SetMiddleMouseButton(bool state)
{
noMiddleMouseButton = state;
if (m_enableConfigSaving) {
m_config->SetInt("noMiddleMouseButton", noMiddleMouseButton);
m_config->Save();
}
}

void Manager::GetMousePosition(int position[2])
{
SDL_GetMouseState(&position[0], &position[1]);
Expand Down Expand Up @@ -846,6 +857,18 @@ void Manager::DispatchEvents()

*/

// Mouse rotation is enabled if:
// MMB is pressed, or Ctrl+LMB is pressed, AND no other mouse buttons are pressed
bool Manager::IsMouseRotatePressed()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am hesitant to accept this. I do not think (philosophically or practically) that it is the role of the Input::Manager to define the conditions for rotating the view across the entire application. I see Input::Manager as being significantly lower-level and this being the responsibility of a higher layer of the application.

Additionally, note that implementing it at this level provides no ability for the user to rebind which modifier key is used when emulating the middle mouse button (perhaps they'd like to use Alt or the meta keys?)

I wish I had an easy solution I could suggest to accomplish this. The mouse rotation code is one of those things that is common to a number of views, but specific enough in scope it's not really a good fit to throw in Pi, Input::Manager, or View. It's game-specific code, but there's a little bit of overlap with the editor as the editor also uses "game" widgets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am hesitant to accept this. I do not think (philosophically or practically) that it is the role of the Input::Manager to define the conditions for rotating the view across the entire application. I see Input::Manager as being significantly lower-level and this being the responsibility of a higher layer of the application.

Ok, just seemed like a natural/easy place for this functionality. It can be placed somewhere else instead. I'm very loathe to re-implement it in every place where it's needed though. I very much dislike code duplication and it already chafes me that I have to duplicate between these views and the Ship Spinner..

Additionally, note that implementing it at this level provides no ability for the user to rebind which modifier key is used when emulating the middle mouse button (perhaps they'd like to use Alt or the meta keys?)

Why not? I haven't gotten around to adding options for rebinding yet but I wasn't aware of there being a technical issue with adding such.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very loathe to re-implement it in every place where it's needed though.

Same. I do think it should be centralized somewhere (perhaps a static method on some sort of helper namespace?) but just not in the low-level input subsystem.

Why not? I haven't gotten around to adding options for rebinding yet but I wasn't aware of there being a technical issue with adding such.

The "no ability" referred to the as-implemented state. In terms of potential implementations, it's not so much that there is a technical problem with it, but rather that the API becomes significantly messier as the Input::Manager now has to export a specific action binding that all code which wants to use the function needs to ensure is present in the InputFrame stack before calling the method.

{
int state = keyModState & KMOD_LCTRL ? 0x01 : 0x00;
state |= MouseButtonState(SDL_BUTTON_LEFT) ? 0x02 : 0x00;
state |= MouseButtonState(SDL_BUTTON_MIDDLE) ? 0x04 : 0x00;
state |= MouseButtonState(SDL_BUTTON_RIGHT) ? 0x08 : 0x00;

return state == 0x03 || state == 0x04;
}

void Manager::SetCapturingMouse(bool grabbed)
{
// early-out to avoid changing (possibly) expensive WM state
Expand All @@ -867,6 +890,13 @@ float Manager::GetMoveSpeedShiftModifier()
{
// Suggestion: make x1000 speed on pressing both keys?
if (KeyState(SDLK_LSHIFT)) return 100.f;
if (KeyState(SDLK_RSHIFT)) return 10.f;
if (KeyState(SDLK_LALT)) return 10.f;
return 1;
}

float Manager::GetRotateSpeedShiftModifier()
{
if (KeyState(SDLK_LSHIFT)) return 10.f;
if (KeyState(SDLK_LALT)) return 0.1f;
return 1;
}
13 changes: 13 additions & 0 deletions src/Input.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ class Input::Manager {
bool IsMouseYInvert() { return mouseYInvert; }
void SetMouseYInvert(bool state);

bool EmulateMiddleMouseButton() { return noMiddleMouseButton; }
void SetMiddleMouseButton(bool state);

bool IsMouseButtonPressed(int button) { return mouseButton[button] == 1; }
bool IsMouseButtonReleased(int button) { return mouseButton[button] == 4; }

Expand All @@ -206,6 +209,11 @@ class Input::Manager {

int GetMouseWheel() { return mouseWheel; }

// Here for unified UI experience.
// Returns true if the mouse button(s)/modifier(s) required to enable
// rotation are currently pressed.
bool IsMouseRotatePressed();

// Capturing the mouse hides the cursor, puts the mouse into relative mode,
// and passes all mouse inputs to the input system, regardless of whether
// ImGui is using them or not.
Expand All @@ -221,6 +229,10 @@ class Input::Manager {
// This is a default value only, centralized here to promote uniform user expericience.
float GetMoveSpeedShiftModifier();

// Get the default speed modifier to apply to rotation depending on the "shift" keys.
// This is a default value only, centralized here to promote uniform user expericience.
float GetRotateSpeedShiftModifier();

sigc::signal<void, SDL_Keysym *> onKeyPress;
sigc::signal<void, SDL_Keysym *> onKeyRelease;
sigc::signal<void, int, int, int> onMouseButtonUp;
Expand All @@ -246,6 +258,7 @@ class Input::Manager {

bool joystickEnabled;
bool mouseYInvert;
bool noMiddleMouseButton;

std::map<std::string, BindingPage> bindingPages;
std::map<std::string, InputBindings::Action> actionBindings;
Expand Down
7 changes: 4 additions & 3 deletions src/SectorMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1151,16 +1151,17 @@ void SectorMap::Update(float frameTime)
if (InputBindings.mapViewPitch->IsActive()) m_rotXMovingTo += 0.5f * moveSpeed * InputBindings.mapViewPitch->GetValue();

// to capture mouse when button was pressed and release when released
if (input->MouseButtonState(SDL_BUTTON_MIDDLE) != m_rotateWithMouseButton) {
if (input->IsMouseRotatePressed() != m_rotateWithMouseButton) {
m_rotateWithMouseButton = !m_rotateWithMouseButton;
input->SetCapturingMouse(m_rotateWithMouseButton);
}

if (m_rotateWithMouseButton || m_rotateView) {
int motion[2];
input->GetMouseMotion(motion);
m_rotXMovingTo += 0.2f * float(motion[1]);
m_rotZMovingTo += 0.2f * float(motion[0]);
float speed = 0.2f * input->GetRotateSpeedShiftModifier();
m_rotXMovingTo += speed * float(motion[1]);
m_rotZMovingTo += speed * float(motion[0]);
} else if (m_zoomView) {
input->SetCapturingMouse(true);
int motion[2];
Expand Down
4 changes: 2 additions & 2 deletions src/SystemView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -843,12 +843,12 @@ void SystemMapViewport::HandleInput(float ft)
Input::Manager *inputMgr = m_app->GetInput();

// to capture mouse when button was pressed and release when released
if (inputMgr->MouseButtonState(SDL_BUTTON_MIDDLE) != m_rotateWithMouseButton) {
if (inputMgr->IsMouseRotatePressed() != m_rotateWithMouseButton) {
m_rotateWithMouseButton = !m_rotateWithMouseButton;
inputMgr->SetCapturingMouse(m_rotateWithMouseButton);
}

float speedMod = inputMgr->KeyState(SDLK_LSHIFT) ? 10.f : (inputMgr->KeyState(SDLK_LCTRL) ? 0.1f : 1.f);
float speedMod = inputMgr->GetRotateSpeedShiftModifier();

if (m_rotateWithMouseButton || m_rotateView) {
int motion[2];
Expand Down
20 changes: 20 additions & 0 deletions src/lua/LuaInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,24 @@ static int l_input_set_mouse_y_inverted(lua_State *l)
return 0;
}


static int l_input_emulate_middle_mouse_button(lua_State *l)
{
lua_pushboolean(l, Pi::input->EmulateMiddleMouseButton());
return 1;
}

static int l_input_set_middle_mouse_button(lua_State *l)
{
if (lua_isnone(l, 1))
return luaL_error(l, "SetMiddleMouseButton takes one boolean argument");
const bool mousebutton = lua_toboolean(l, 1);
Pi::config->SetInt("noMiddleMouseButton", (mousebutton ? 1 : 0));
Pi::config->Save();
Pi::input->SetMiddleMouseButton(mousebutton);
return 0;
}

static int l_input_get_mouse_captured(lua_State *l)
{
LuaPush<bool>(l, Pi::input->IsCapturingMouse());
Expand Down Expand Up @@ -1122,6 +1140,8 @@ void LuaInput::Register()
{ "SaveBinding", l_input_save_binding },
{ "GetMouseYInverted", l_input_get_mouse_y_inverted },
{ "SetMouseYInverted", l_input_set_mouse_y_inverted },
{ "EmulateMiddleMouseButton", l_input_emulate_middle_mouse_button },
{ "SetMiddleMouseButton", l_input_set_middle_mouse_button },
{ "GetJoystickEnabled", l_input_get_joystick_enabled },
{ "SetJoystickEnabled", l_input_set_joystick_enabled },

Expand Down
11 changes: 10 additions & 1 deletion src/pigui/ModelSpinner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "graphics/RenderTarget.h"
#include "graphics/Renderer.h"
#include "graphics/Texture.h"
#include "Input.h"
#include "scenegraph/Tag.h"

#include <algorithm>
Expand All @@ -17,6 +18,8 @@ using namespace PiGui;

ModelSpinner::ModelSpinner() :
m_spinning(true),
m_middleMouseButton(false),
m_rotateWithMouseButton(false),
m_pauseTime(.0f),
m_rot(vector2f(DEG2RAD(-15.0), DEG2RAD(120.0))),
m_zoom(1.0f),
Expand Down Expand Up @@ -136,7 +139,13 @@ void ModelSpinner::DrawPiGui()

const ImGuiIO &io = ImGui::GetIO();
bool hovered = ImGui::IsItemHovered();
if (hovered && ImGui::IsMouseDown(0)) {
// TODO: This should be unified with Input::Manager::IsMouseRotatePressed()
bool isMouseRotatePressed = ImGui::IsMouseDown(2) || ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsMouseDown(0);
if (hovered && isMouseRotatePressed != m_rotateWithMouseButton) {
m_rotateWithMouseButton = !m_rotateWithMouseButton;
Pi::input->SetCapturingMouse(m_rotateWithMouseButton);
}
if (m_rotateWithMouseButton) {
m_rot.x -= 0.005 * io.MouseDelta.y;
m_rot.y -= 0.005 * io.MouseDelta.x;
m_pauseTime = 1.0f;
Expand Down
6 changes: 6 additions & 0 deletions src/pigui/ModelSpinner.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ namespace PiGui {
// Shoulde we spinne?
bool m_spinning;

// Is there a middle mouse button?
bool m_middleMouseButton;

// Is the user rotating the model?
bool m_rotateWithMouseButton;

// After the user manually rotates the model, hold that orientation for
// a second to let them look at it. Assumes Update() is called every
// frame while visible.
Expand Down
7 changes: 4 additions & 3 deletions src/ship/ShipViewController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ void ShipViewController::Update()
Pi::input->GetMouseMotion(mouseMotion);

// external camera mouselook
bool mouse_down = Pi::input->MouseButtonState(SDL_BUTTON_MIDDLE);
bool mouse_down = Pi::input->IsMouseRotatePressed();
if (mouse_down && !headtracker_input_priority) {
if (!m_mouseActive) {
m_mouseActive = true;
Expand All @@ -282,8 +282,9 @@ void ShipViewController::Update()

// invert the mouse input to convert between screen coordinates and
// right-hand coordinate system rotation.
cam->YawCamera(float(-mouseMotion[0]) * MOUSELOOK_SPEED / M_PI);
cam->PitchCamera(float(-mouseMotion[1]) * MOUSELOOK_SPEED / M_PI);
float speed = MOUSELOOK_SPEED * Pi::input->GetRotateSpeedShiftModifier();
cam->YawCamera(float(-mouseMotion[0]) * speed / M_PI);
cam->PitchCamera(float(-mouseMotion[1]) * speed / M_PI);
}

if (!mouse_down && m_mouseActive) {
Expand Down