mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
recording: Factor in DPIScaling when scaling VirtualPad window recording: Add a reset button, hopefully better quality backgrounds for lower resolutions recording: Improved VPad analog alignment Co-authored-by: sonicfind recording: Some final VirtualPad coordinate adjustments
439 lines
19 KiB
C++
439 lines
19 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "PrecompiledHeader.h"
|
|
|
|
#ifndef DISABLE_RECORDING
|
|
|
|
#include <math.h>
|
|
|
|
#include "App.h"
|
|
#include "MSWstuff.h"
|
|
#include "Utilities/EmbeddedImage.h"
|
|
#include "wx/dcbuffer.h"
|
|
#include "wx/display.h"
|
|
#include "wx/spinctrl.h"
|
|
|
|
#include "Recording/VirtualPad/VirtualPad.h"
|
|
#include "Recording/VirtualPad/VirtualPadResources.h"
|
|
|
|
#include "Recording/VirtualPad/img/circlePressed.h"
|
|
#include "Recording/VirtualPad/img/controllerFull.h"
|
|
#include "Recording/VirtualPad/img/controllerThreeQuarters.h"
|
|
#include "Recording/VirtualPad/img/controllerHalf.h"
|
|
#include "Recording/VirtualPad/img/crossPressed.h"
|
|
#include "Recording/VirtualPad/img/downPressed.h"
|
|
#include "Recording/VirtualPad/img/l1Pressed.h"
|
|
#include "Recording/VirtualPad/img/l2Pressed.h"
|
|
#include "Recording/VirtualPad/img/l3Pressed.h"
|
|
#include "Recording/VirtualPad/img/leftPressed.h"
|
|
#include "Recording/VirtualPad/img/r1Pressed.h"
|
|
#include "Recording/VirtualPad/img/r2Pressed.h"
|
|
#include "Recording/VirtualPad/img/r3Pressed.h"
|
|
#include "Recording/VirtualPad/img/rightPressed.h"
|
|
#include "Recording/VirtualPad/img/selectPressed.h"
|
|
#include "Recording/VirtualPad/img/squarePressed.h"
|
|
#include "Recording/VirtualPad/img/startPressed.h"
|
|
#include "Recording/VirtualPad/img/trianglePressed.h"
|
|
#include "Recording/VirtualPad/img/upPressed.h"
|
|
|
|
|
|
VirtualPad::VirtualPad(wxWindow* parent, int controllerPort, AppConfig::InputRecordingOptions& options)
|
|
: wxFrame(parent, wxID_ANY, wxEmptyString)
|
|
, options(options)
|
|
{
|
|
// Images at 1.00 scale are designed to work well on HiDPI (4k) at 150% scaling (default recommended setting on windows)
|
|
// Therefore, on a 1080p monitor we halve the scaling, on 1440p we reduce it by 25%, which from some quick tests looks comparable
|
|
// Side-note - Getting the DPI scaling amount is platform specific (with some platforms only supporting
|
|
// integer scaling as well) this is likely not reliable.
|
|
// Slight multi-monitor support, will use whatever window pcsx2 is opened with, but won't currently re-init if
|
|
// windows are dragged between differing monitors!
|
|
wxDisplay display(wxDisplay::GetFromWindow(this));
|
|
const wxRect screen = display.GetClientArea();
|
|
float dpiScale = MSW_GetDPIScale(); // linux returns 1.0
|
|
if (screen.height > 1080 && screen.height <= 1440) // 1440p display
|
|
scalingFactor = 0.75 * dpiScale;
|
|
else if (screen.height <= 1080) // 1080p display
|
|
{
|
|
scalingFactor = 0.5 * dpiScale;
|
|
}
|
|
// otherwise use default 1.0 scaling
|
|
|
|
virtualPadData = VirtualPadData();
|
|
// Based on the scaling factor, select the appropriate background image
|
|
// Don't scale these images as they've already been pre-scaled
|
|
if (floatCompare(scalingFactor, 0.5))
|
|
virtualPadData.background = NewBitmap(EmbeddedImage<res_controllerHalf>().Get(), wxPoint(0, 0), true);
|
|
else if (floatCompare(scalingFactor, 0.75))
|
|
virtualPadData.background = NewBitmap(EmbeddedImage<res_controllerThreeQuarters>().Get(), wxPoint(0, 0), true);
|
|
else
|
|
// Otherwise, scale down/up (or don't in the case of 1.0) the largst image
|
|
virtualPadData.background = NewBitmap(EmbeddedImage<res_controllerFull>().Get(), wxPoint(0, 0));
|
|
|
|
// Use the background image's size to define the window size
|
|
SetClientSize(virtualPadData.background.width, virtualPadData.background.height);
|
|
|
|
// These hard-coded pixels correspond to where the background image's components are (ie. the buttons)
|
|
// Everything is automatically scaled and adjusted based on the `scalingFactor` variable
|
|
InitPressureButtonGuiElements(virtualPadData.square, NewBitmap(EmbeddedImage<res_squarePressed>().Get(), wxPoint(852, 287)), this, wxPoint(1055, 525));
|
|
InitPressureButtonGuiElements(virtualPadData.triangle, NewBitmap(EmbeddedImage<res_trianglePressed>().Get(), wxPoint(938, 201)), this, wxPoint(1055, 565));
|
|
InitPressureButtonGuiElements(virtualPadData.circle, NewBitmap(EmbeddedImage<res_circlePressed>().Get(), wxPoint(1024, 286)), this, wxPoint(1055, 605));
|
|
InitPressureButtonGuiElements(virtualPadData.cross, NewBitmap(EmbeddedImage<res_crossPressed>().Get(), wxPoint(938, 369)), this, wxPoint(1055, 645));
|
|
|
|
InitPressureButtonGuiElements(virtualPadData.left, NewBitmap(EmbeddedImage<res_leftPressed>().Get(), wxPoint(110, 303)), this, wxPoint(175, 525), true);
|
|
InitPressureButtonGuiElements(virtualPadData.up, NewBitmap(EmbeddedImage<res_upPressed>().Get(), wxPoint(186, 227)), this, wxPoint(175, 565), true);
|
|
InitPressureButtonGuiElements(virtualPadData.right, NewBitmap(EmbeddedImage<res_rightPressed>().Get(), wxPoint(248, 302)), this, wxPoint(175, 605), true);
|
|
InitPressureButtonGuiElements(virtualPadData.down, NewBitmap(EmbeddedImage<res_downPressed>().Get(), wxPoint(186, 359)), this, wxPoint(175, 645), true);
|
|
|
|
InitPressureButtonGuiElements(virtualPadData.l1, NewBitmap(EmbeddedImage<res_l1Pressed>().Get(), wxPoint(156, 98)), this, wxPoint(170, 135));
|
|
InitPressureButtonGuiElements(virtualPadData.l2, NewBitmap(EmbeddedImage<res_l2Pressed>().Get(), wxPoint(156, 57)), this, wxPoint(170, 52), false, true);
|
|
InitPressureButtonGuiElements(virtualPadData.r1, NewBitmap(EmbeddedImage<res_r1Pressed>().Get(), wxPoint(921, 98)), this, wxPoint(1035, 135), true);
|
|
InitPressureButtonGuiElements(virtualPadData.r2, NewBitmap(EmbeddedImage<res_r2Pressed>().Get(), wxPoint(921, 57)), this, wxPoint(1035, 52), true, true);
|
|
|
|
InitNormalButtonGuiElements(virtualPadData.select, NewBitmap(EmbeddedImage<res_selectPressed>().Get(), wxPoint(458, 313)), this, wxPoint(530, 315));
|
|
InitNormalButtonGuiElements(virtualPadData.start, NewBitmap(EmbeddedImage<res_startPressed>().Get(), wxPoint(688, 311)), this, wxPoint(646, 315));
|
|
InitNormalButtonGuiElements(virtualPadData.l3, NewBitmap(EmbeddedImage<res_l3Pressed>().Get(), wxPoint(336, 453)), this, wxPoint(560, 638));
|
|
InitNormalButtonGuiElements(virtualPadData.r3, NewBitmap(EmbeddedImage<res_r3Pressed>().Get(), wxPoint(726, 453)), this, wxPoint(615, 638));
|
|
|
|
InitAnalogStickGuiElements(virtualPadData.leftAnalog, this, wxPoint(404, 522), 100, wxPoint(314, 642), wxPoint(526, 432), false, wxPoint(504, 685), wxPoint(570, 425), true);
|
|
InitAnalogStickGuiElements(virtualPadData.rightAnalog, this, wxPoint(794, 522), 100, wxPoint(706, 642), wxPoint(648, 432), true, wxPoint(700, 685), wxPoint(635, 425));
|
|
|
|
ignoreRealControllerBox = new wxCheckBox(this, wxID_ANY, wxEmptyString, ScaledPoint(wxPoint(586, 135)), wxDefaultSize);
|
|
resetButton = new wxButton(this, wxID_ANY, _("Reset"), ScaledPoint(wxPoint(1195, 5), wxSize(100, 50), true), ScaledSize(wxSize(100, 50)));
|
|
|
|
Bind(wxEVT_CHECKBOX, &VirtualPad::OnIgnoreRealController, this, ignoreRealControllerBox->GetId());
|
|
Bind(wxEVT_BUTTON, &VirtualPad::OnResetButton, this, resetButton->GetId());
|
|
|
|
// Bind Window Events
|
|
Bind(wxEVT_MOVE, &VirtualPad::OnMoveAround, this);
|
|
Bind(wxEVT_CLOSE_WINDOW, &VirtualPad::OnClose, this);
|
|
Bind(wxEVT_ICONIZE, &VirtualPad::OnIconize, this);
|
|
Bind(wxEVT_ERASE_BACKGROUND, &VirtualPad::OnEraseBackground, this);
|
|
// Temporary Paint event handler so the window displays properly before the controller-interrupt routine takes over with manual drawing.
|
|
// The reason for this is in order to minimize the performance impact, we need total control over when render is called
|
|
// Windows redraws the window _alot_ otherwise which leads to major performance problems (when GS is using the software renderer)
|
|
Bind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
|
|
// DevCon.WriteLn("Paint Event Bound");
|
|
|
|
// Finalize layout
|
|
SetIcons(wxGetApp().GetIconBundle());
|
|
SetTitle(wxString::Format("Virtual Pad - Port %d", controllerPort + 1));
|
|
SetPosition(options.VirtualPadPosition);
|
|
SetBackgroundColour(*wxWHITE);
|
|
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
// This window does not allow for resizing for sake of simplicity: all images are scaled initially and stored, ready to be rendered
|
|
SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~wxRESIZE_BORDER & ~wxMAXIMIZE_BOX);
|
|
|
|
// Causes flickering, despite it supposed to be preventing it!
|
|
// SetDoubleBuffered(true);
|
|
}
|
|
|
|
void VirtualPad::OnMoveAround(wxMoveEvent& event)
|
|
{
|
|
if (IsBeingDeleted() || !IsVisible() || IsIconized())
|
|
return;
|
|
|
|
if (!IsMaximized())
|
|
options.VirtualPadPosition = GetPosition();
|
|
event.Skip();
|
|
}
|
|
|
|
void VirtualPad::OnClose(wxCloseEvent& event)
|
|
{
|
|
// Re-bind the Paint event in case this is due to a game being opened/closed
|
|
manualRedrawMode = false;
|
|
Bind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
|
|
// DevCon.WriteLn("Paint Event Bound");
|
|
Hide();
|
|
}
|
|
|
|
void VirtualPad::OnIconize(wxIconizeEvent& event)
|
|
{
|
|
if (event.IsIconized())
|
|
{
|
|
manualRedrawMode = false;
|
|
Bind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
|
|
// DevCon.WriteLn("Paint Event Bound");
|
|
}
|
|
}
|
|
|
|
void VirtualPad::OnEraseBackground(wxEraseEvent& event)
|
|
{
|
|
// Intentionally Empty
|
|
// See - https://wiki.wxwidgets.org/Flicker-Free_Drawing
|
|
}
|
|
|
|
void VirtualPad::OnPaint(wxPaintEvent& event)
|
|
{
|
|
// DevCon.WriteLn("Paint Event Called");
|
|
wxBufferedPaintDC dc(this, wxBUFFER_VIRTUAL_AREA);
|
|
Render(dc);
|
|
}
|
|
|
|
void VirtualPad::Redraw()
|
|
{
|
|
wxClientDC cdc(this);
|
|
wxBufferedDC dc(&cdc);
|
|
Render(dc);
|
|
}
|
|
|
|
void VirtualPad::Render(wxDC& bdc)
|
|
{
|
|
// Update GUI Elements and figure out what needs to be rendered
|
|
for (VirtualPadElement* virtualPadElement : virtualPadElements)
|
|
virtualPadElement->UpdateGuiElement(renderQueue, clearScreenRequired);
|
|
|
|
// Update Graphic Elements off render stack
|
|
// Before we start rendering (if we have to) clear and re-draw the background
|
|
if (!manualRedrawMode || clearScreenRequired || !renderQueue.empty())
|
|
{
|
|
bdc.SetBrush(*wxWHITE);
|
|
bdc.DrawRectangle(wxPoint(0, 0), bdc.GetSize());
|
|
bdc.SetBrush(wxNullBrush);
|
|
bdc.DrawBitmap(virtualPadData.background.image, virtualPadData.background.coords, true);
|
|
clearScreenRequired = false;
|
|
|
|
// Switch to Manual Rendering once the first user action on the VirtualPad is taken
|
|
if (!manualRedrawMode && !renderQueue.empty())
|
|
{
|
|
// DevCon.WriteLn("Paint Event Unbound");
|
|
Unbind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
|
|
manualRedrawMode = true;
|
|
}
|
|
|
|
// NOTE - there is yet another (and I think final) micro-optimization that can be done:
|
|
// It can be assumed that if the element has already been drawn to the screen (and not cleared) that we can skip rendering it
|
|
//
|
|
// For example - you hold a single button for several frames, it will currently draw that every frame
|
|
// despite the screen never being cleared - so this is not strictly necessary.
|
|
//
|
|
// Though after some tests, the performance impact is well within reason, and on the hardware renderer modes, is almost non-existant.
|
|
while (!renderQueue.empty())
|
|
{
|
|
VirtualPadElement* element = renderQueue.front();
|
|
if (element)
|
|
element->Render(bdc);
|
|
renderQueue.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool VirtualPad::UpdateControllerData(u16 const bufIndex, PadData* padData)
|
|
{
|
|
return virtualPadData.UpdateVirtualPadData(bufIndex, padData, ignoreRealController && !readOnlyMode, readOnlyMode);
|
|
}
|
|
|
|
void VirtualPad::enableUiElements(bool enable)
|
|
{
|
|
ignoreRealControllerBox->Enable(enable);
|
|
resetButton->Enable(enable);
|
|
for (VirtualPadElement* virtualPadElement : virtualPadElements)
|
|
virtualPadElement->EnableWidgets(enable);
|
|
}
|
|
|
|
void VirtualPad::SetReadOnlyMode(bool readOnly)
|
|
{
|
|
enableUiElements(!readOnly);
|
|
readOnlyMode = readOnly;
|
|
}
|
|
|
|
void VirtualPad::OnIgnoreRealController(wxCommandEvent& event)
|
|
{
|
|
const wxCheckBox* ignoreButton = (wxCheckBox*)event.GetEventObject();
|
|
if (ignoreButton)
|
|
ignoreRealController = ignoreButton->GetValue();
|
|
}
|
|
|
|
void VirtualPad::OnResetButton(wxCommandEvent& event)
|
|
{
|
|
if (readOnlyMode)
|
|
return;
|
|
|
|
for (VirtualPadElement* virtualPadElement : virtualPadElements)
|
|
virtualPadElement->Reset(this);
|
|
}
|
|
|
|
void VirtualPad::OnNormalButtonPress(wxCommandEvent& event)
|
|
{
|
|
const wxCheckBox* pressedButton = (wxCheckBox*)event.GetEventObject();
|
|
ControllerNormalButton* eventBtn = buttonElements[pressedButton->GetId()];
|
|
|
|
if (pressedButton)
|
|
eventBtn->pressed = pressedButton->GetValue();
|
|
|
|
if (!eventBtn->isControllerPressBypassed)
|
|
eventBtn->isControllerPressBypassed = true;
|
|
}
|
|
|
|
void VirtualPad::OnPressureButtonPressureChange(wxCommandEvent& event)
|
|
{
|
|
const wxSpinCtrl* pressureSpinner = (wxSpinCtrl*)event.GetEventObject();
|
|
ControllerPressureButton* eventBtn = pressureElements[pressureSpinner->GetId()];
|
|
|
|
if (pressureSpinner)
|
|
eventBtn->pressure = pressureSpinner->GetValue();
|
|
|
|
eventBtn->pressed = eventBtn->pressure > 0;
|
|
|
|
if (!eventBtn->isControllerPressureBypassed || !eventBtn->isControllerPressBypassed)
|
|
{
|
|
eventBtn->isControllerPressureBypassed = true;
|
|
eventBtn->isControllerPressBypassed = true;
|
|
}
|
|
}
|
|
|
|
void VirtualPad::OnAnalogSpinnerChange(wxCommandEvent& event)
|
|
{
|
|
const wxSpinCtrl* analogSpinner = (wxSpinCtrl*)event.GetEventObject();
|
|
AnalogVector* eventVector = analogElements[analogSpinner->GetId()];
|
|
|
|
if (analogSpinner)
|
|
eventVector->val = analogSpinner->GetValue();
|
|
|
|
eventVector->slider->SetValue(eventVector->val);
|
|
|
|
if (!eventVector->isControllerBypassed)
|
|
eventVector->isControllerBypassed = true;
|
|
}
|
|
|
|
void VirtualPad::OnAnalogSliderChange(wxCommandEvent& event)
|
|
{
|
|
const wxSlider* analogSlider = (wxSlider*)event.GetEventObject();
|
|
AnalogVector* eventVector = analogElements[analogSlider->GetId()];
|
|
|
|
if (analogSlider)
|
|
eventVector->val = analogSlider->GetValue();
|
|
|
|
eventVector->spinner->SetValue(eventVector->val);
|
|
|
|
if (!eventVector->isControllerBypassed)
|
|
eventVector->isControllerBypassed = true;
|
|
}
|
|
|
|
/// GUI Element Utility Functions
|
|
|
|
bool VirtualPad::floatCompare(float a, float b, float epsilon)
|
|
{
|
|
return (fabs(a - b) < epsilon);
|
|
}
|
|
|
|
wxPoint VirtualPad::ScaledPoint(wxPoint point, wxSize widgetWidth, bool rightAlignedCoord, bool bottomAlignedCoord)
|
|
{
|
|
return ScaledPoint(point.x, point.y, widgetWidth.x, widgetWidth.y, rightAlignedCoord, bottomAlignedCoord);
|
|
}
|
|
|
|
wxPoint VirtualPad::ScaledPoint(int x, int y, int widgetWidth, int widgetHeight, bool rightAlignedCoord, bool bottomAlignedCoord)
|
|
{
|
|
wxPoint scaledPoint = wxPoint(x * scalingFactor, y * scalingFactor);
|
|
if (rightAlignedCoord)
|
|
{
|
|
scaledPoint.x -= widgetWidth * scalingFactor;
|
|
if (scaledPoint.x < 0)
|
|
scaledPoint.x = 0;
|
|
}
|
|
if (bottomAlignedCoord)
|
|
{
|
|
scaledPoint.y -= widgetHeight * scalingFactor;
|
|
if (scaledPoint.y < 0)
|
|
scaledPoint.y = 0;
|
|
}
|
|
return scaledPoint;
|
|
}
|
|
|
|
wxSize VirtualPad::ScaledSize(wxSize size)
|
|
{
|
|
return ScaledSize(size.x, size.y);
|
|
}
|
|
|
|
wxSize VirtualPad::ScaledSize(int x, int y)
|
|
{
|
|
return wxSize(x * scalingFactor, y * scalingFactor);
|
|
}
|
|
|
|
ImageFile VirtualPad::NewBitmap(wxImage resource, wxPoint imgCoord, bool dontScale)
|
|
{
|
|
return NewBitmap(dontScale ? 1 : scalingFactor, resource, imgCoord);
|
|
}
|
|
|
|
ImageFile VirtualPad::NewBitmap(float scalingFactor, wxImage resource, wxPoint imgCoord)
|
|
{
|
|
wxBitmap bitmap = wxBitmap(resource.Rescale(resource.GetWidth() * scalingFactor, resource.GetHeight() * scalingFactor, wxIMAGE_QUALITY_HIGH));
|
|
|
|
ImageFile image = ImageFile();
|
|
image.image = bitmap;
|
|
image.width = bitmap.GetWidth();
|
|
image.height = bitmap.GetHeight();
|
|
image.coords = ScaledPoint(imgCoord);
|
|
return image;
|
|
}
|
|
|
|
void VirtualPad::InitNormalButtonGuiElements(ControllerNormalButton& button, ImageFile image, wxWindow* parentWindow, wxPoint checkboxCoord)
|
|
{
|
|
button.icon = image;
|
|
button.pressedBox = new wxCheckBox(parentWindow, wxID_ANY, wxEmptyString, ScaledPoint(checkboxCoord), wxDefaultSize);
|
|
Bind(wxEVT_CHECKBOX, &VirtualPad::OnNormalButtonPress, this, button.pressedBox->GetId());
|
|
buttonElements[button.pressedBox->GetId()] = &button;
|
|
virtualPadElements.push_back(&button);
|
|
}
|
|
|
|
void VirtualPad::InitPressureButtonGuiElements(ControllerPressureButton& button, ImageFile image, wxWindow* parentWindow, wxPoint pressureSpinnerCoord, bool rightAlignedCoord, bool bottomAlignedCoord)
|
|
{
|
|
const wxPoint scaledPoint = ScaledPoint(pressureSpinnerCoord, SPINNER_SIZE, rightAlignedCoord, bottomAlignedCoord);
|
|
wxSpinCtrl* spinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, scaledPoint, ScaledSize(SPINNER_SIZE), wxSP_ARROW_KEYS, 0, 255, 0);
|
|
|
|
button.icon = image;
|
|
button.pressureSpinner = spinner;
|
|
Bind(wxEVT_SPINCTRL, &VirtualPad::OnPressureButtonPressureChange, this, button.pressureSpinner->GetId());
|
|
pressureElements[button.pressureSpinner->GetId()] = &button;
|
|
virtualPadElements.push_back(&button);
|
|
}
|
|
|
|
void VirtualPad::InitAnalogStickGuiElements(AnalogStick& analog, wxWindow* parentWindow, wxPoint centerPoint, int radius,
|
|
wxPoint xSliderPoint, wxPoint ySliderPoint, bool flipYSlider, wxPoint xSpinnerPoint, wxPoint ySpinnerPoint, bool rightAlignedSpinners)
|
|
{
|
|
AnalogPosition analogPos = AnalogPosition();
|
|
analogPos.centerCoords = ScaledPoint(centerPoint);
|
|
analogPos.endCoords = ScaledPoint(centerPoint);
|
|
analogPos.radius = radius * scalingFactor;
|
|
analogPos.lineThickness = 6 * scalingFactor;
|
|
|
|
const wxPoint xSpinnerScaledPoint = ScaledPoint(xSpinnerPoint, SPINNER_SIZE, rightAlignedSpinners);
|
|
const wxPoint ySpinnerScaledPoint = ScaledPoint(ySpinnerPoint, SPINNER_SIZE, rightAlignedSpinners, true);
|
|
|
|
wxSlider* xSlider = new wxSlider(parentWindow, wxID_ANY, ANALOG_NEUTRAL, 0, ANALOG_MAX,
|
|
ScaledPoint(xSliderPoint), ScaledSize(ANALOG_SLIDER_WIDTH, ANALOG_SLIDER_HEIGHT));
|
|
wxSlider* ySlider = new wxSlider(parentWindow, wxID_ANY, ANALOG_NEUTRAL, 0, ANALOG_MAX,
|
|
ScaledPoint(ySliderPoint), ScaledSize(ANALOG_SLIDER_HEIGHT, ANALOG_SLIDER_WIDTH), flipYSlider ? wxSL_LEFT : wxSL_RIGHT);
|
|
wxSpinCtrl* xSpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, xSpinnerScaledPoint, ScaledSize(SPINNER_SIZE), wxSP_ARROW_KEYS, 0, 255, 127);
|
|
wxSpinCtrl* ySpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, ySpinnerScaledPoint, ScaledSize(SPINNER_SIZE), wxSP_ARROW_KEYS, 0, 255, 127);
|
|
|
|
analog.xVector.slider = xSlider;
|
|
analog.yVector.slider = ySlider;
|
|
analog.xVector.spinner = xSpinner;
|
|
analog.yVector.spinner = ySpinner;
|
|
analog.positionGraphic = analogPos;
|
|
Bind(wxEVT_SLIDER, &VirtualPad::OnAnalogSliderChange, this, xSlider->GetId());
|
|
Bind(wxEVT_SLIDER, &VirtualPad::OnAnalogSliderChange, this, ySlider->GetId());
|
|
Bind(wxEVT_SPINCTRL, &VirtualPad::OnAnalogSpinnerChange, this, xSpinner->GetId());
|
|
Bind(wxEVT_SPINCTRL, &VirtualPad::OnAnalogSpinnerChange, this, ySpinner->GetId());
|
|
analogElements[xSlider->GetId()] = &analog.xVector;
|
|
analogElements[ySlider->GetId()] = &analog.yVector;
|
|
analogElements[xSpinner->GetId()] = &analog.xVector;
|
|
analogElements[ySpinner->GetId()] = &analog.yVector;
|
|
virtualPadElements.push_back(&analog);
|
|
}
|
|
|
|
#endif
|