summaryrefslogtreecommitdiff
path: root/Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp
diff options
context:
space:
mode:
authorMat M <mathew1800@gmail.com>2016-11-29 07:39:28 -0500
committerGitHub <noreply@github.com>2016-11-29 07:39:28 -0500
commiteebe4ef1fd569bbcb8c87d961de866d687c58354 (patch)
tree1150a54cc20150e49cec80ca21ee548d4c66cc67 /Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp
parent0212741574548fa95593e0e9d9451feca45ab83d (diff)
parent32a0dae257c0454e38014b97709580175b8895a5 (diff)
Merge pull request #4461 from aldelaro5/hotkey-config-redo
Redo every input configuration dialog
Diffstat (limited to 'Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp')
-rw-r--r--Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp539
1 files changed, 539 insertions, 0 deletions
diff --git a/Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp b/Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp
new file mode 100644
index 0000000000..f531e8c1a0
--- /dev/null
+++ b/Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp
@@ -0,0 +1,539 @@
+// Copyright 2010 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cmath>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <vector>
+#include <wx/bitmap.h>
+#include <wx/brush.h>
+#include <wx/colour.h>
+#include <wx/dcmemory.h>
+#include <wx/font.h>
+#include <wx/graphics.h>
+#include <wx/notebook.h>
+#include <wx/pen.h>
+#include <wx/settings.h>
+#include <wx/statbmp.h>
+
+#include "DolphinWX/Input/InputConfigDiag.h"
+#include "DolphinWX/WxUtils.h"
+
+#include "InputCommon/ControllerEmu.h"
+#include "InputCommon/ControllerInterface/ControllerInterface.h"
+#include "InputCommon/ControllerInterface/Device.h"
+
+struct ShapePosition
+{
+ double max;
+ double diag;
+ double box;
+ double scale;
+
+ double dz;
+ double range;
+
+ wxCoord offset;
+};
+
+// regular octagon
+static void DrawOctagon(wxGraphicsContext* gc, ShapePosition p)
+{
+ static constexpr int vertices = 8;
+ double radius = p.max;
+
+ wxGraphicsPath path = gc->CreatePath();
+
+ double angle = 2.0 * M_PI / vertices;
+
+ for (int i = 0; i < vertices; i++)
+ {
+ double a = angle * i;
+ double x = radius * cos(a);
+ double y = radius * sin(a);
+ if (i == 0)
+ path.MoveToPoint(x, y);
+ else
+ path.AddLineToPoint(x, y);
+ }
+ path.CloseSubpath();
+
+ wxGraphicsMatrix matrix = gc->CreateMatrix();
+ matrix.Translate(p.offset, p.offset);
+ path.Transform(matrix);
+
+ gc->DrawPath(path);
+}
+
+// irregular dodecagon
+static void DrawDodecagon(wxGraphicsContext* gc, ShapePosition p)
+{
+ wxGraphicsPath path = gc->CreatePath();
+ path.MoveToPoint(p.dz, p.max);
+ path.AddLineToPoint(p.diag, p.diag);
+ path.AddLineToPoint(p.max, p.dz);
+ path.AddLineToPoint(p.max, -p.dz);
+ path.AddLineToPoint(p.diag, -p.diag);
+ path.AddLineToPoint(p.dz, -p.max);
+ path.AddLineToPoint(-p.dz, -p.max);
+ path.AddLineToPoint(-p.diag, -p.diag);
+ path.AddLineToPoint(-p.max, -p.dz);
+ path.AddLineToPoint(-p.max, p.dz);
+ path.AddLineToPoint(-p.diag, p.diag);
+ path.AddLineToPoint(-p.dz, p.max);
+ path.CloseSubpath();
+
+ wxGraphicsMatrix matrix = gc->CreateMatrix();
+ matrix.Translate(p.offset, p.offset);
+ path.Transform(matrix);
+
+ gc->DrawPath(path);
+}
+
+static void DrawCenteredRectangle(wxGraphicsContext* gc, double x, double y, double w, double h)
+{
+ x -= w / 2;
+ y -= h / 2;
+ gc->DrawRectangle(x, y, w, h);
+}
+
+#define VIS_BITMAP_SIZE 64
+
+#define VIS_NORMALIZE(a) ((a / 2.0) + 0.5)
+#define VIS_COORD(a) ((VIS_NORMALIZE(a)) * VIS_BITMAP_SIZE)
+
+#define COORD_VIS_SIZE 4
+
+static void DrawCoordinate(wxGraphicsContext* gc, ControlState x, ControlState y)
+{
+ double xc = VIS_COORD(x);
+ double yc = VIS_COORD(y);
+ DrawCenteredRectangle(gc, xc, yc, COORD_VIS_SIZE, COORD_VIS_SIZE);
+}
+
+static void DrawButton(const std::vector<unsigned int>& bitmasks, unsigned int buttons,
+ unsigned int n, wxGraphicsContext* gc, ControlGroupBox* g, unsigned int row,
+ const wxGraphicsMatrix& null_matrix)
+{
+ if (buttons & bitmasks[(row * 8) + n])
+ {
+ gc->SetBrush(*wxRED_BRUSH);
+ }
+ else
+ {
+ unsigned char amt = 255 - g->control_group->controls[(row * 8) + n]->control_ref->State() * 128;
+ gc->SetBrush(wxBrush(wxColour(amt, amt, amt)));
+ }
+ gc->DrawRectangle(n * 12, (row == 0) ? 0 : (row * 11), 14, 12);
+
+ // text
+ const std::string name = g->control_group->controls[(row * 8) + n]->name;
+ // Matrix transformation needs to be disabled so we don't draw scaled/zoomed text.
+ wxGraphicsMatrix old_matrix = gc->GetTransform();
+ gc->SetTransform(null_matrix);
+ // bit of hax so ZL, ZR show up as L, R
+ gc->DrawText(wxUniChar((name[1] && name[1] < 'a') ? name[1] : name[0]), (n * 12 + 2) * g->m_scale,
+ (1 + (row == 0 ? 0 : row * 11)) * g->m_scale);
+ gc->SetTransform(old_matrix);
+}
+
+static void DrawControlGroupBox(wxGraphicsContext* gc, ControlGroupBox* g)
+{
+ wxGraphicsMatrix null_matrix = gc->GetTransform();
+ wxGraphicsMatrix scale_matrix = null_matrix;
+ scale_matrix.Scale(g->m_scale, g->m_scale);
+
+ gc->SetTransform(scale_matrix);
+
+ switch (g->control_group->type)
+ {
+ case GROUP_TYPE_TILT:
+ case GROUP_TYPE_STICK:
+ case GROUP_TYPE_CURSOR:
+ {
+ // this is starting to be a mess combining all these in one case
+
+ ControlState x = 0, y = 0, z = 0;
+
+ switch (g->control_group->type)
+ {
+ case GROUP_TYPE_STICK:
+ ((ControllerEmu::AnalogStick*)g->control_group)->GetState(&x, &y);
+ break;
+ case GROUP_TYPE_TILT:
+ ((ControllerEmu::Tilt*)g->control_group)->GetState(&x, &y);
+ break;
+ case GROUP_TYPE_CURSOR:
+ ((ControllerEmu::Cursor*)g->control_group)->GetState(&x, &y, &z);
+ break;
+ }
+
+ // ir cursor forward movement
+ if (GROUP_TYPE_CURSOR == g->control_group->type)
+ {
+ gc->SetBrush(z ? *wxRED_BRUSH : *wxGREY_BRUSH);
+ wxGraphicsPath path = gc->CreatePath();
+ path.AddRectangle(0, 31 - z * 31, 64, 2);
+ gc->FillPath(path);
+ }
+
+ // input zone
+ gc->SetPen(*wxLIGHT_GREY_PEN);
+ if (GROUP_TYPE_STICK == g->control_group->type)
+ {
+ gc->SetBrush(wxColour(0xDDDDDD)); // Light Gray
+
+ ShapePosition p;
+ p.box = 64;
+ p.offset = p.box / 2;
+ p.range = 256;
+ p.scale = p.box / p.range;
+ p.dz = 15 * p.scale;
+ bool octagon = false;
+
+ if (g->control_group->name == "Main Stick")
+ {
+ p.max = 87 * p.scale;
+ p.diag = 55 * p.scale;
+ }
+ else if (g->control_group->name == "C-Stick")
+ {
+ p.max = 74 * p.scale;
+ p.diag = 46 * p.scale;
+ }
+ else
+ {
+ p.scale = 1;
+ p.max = 32;
+ octagon = true;
+ }
+
+ if (octagon)
+ DrawOctagon(gc, p);
+ else
+ DrawDodecagon(gc, p);
+ }
+ else
+ {
+ gc->SetBrush(*wxWHITE_BRUSH);
+ gc->DrawRectangle(16, 16, 32, 32);
+ }
+
+ if (GROUP_TYPE_CURSOR != g->control_group->type)
+ {
+ int deadzone_idx = g->control_group->type == GROUP_TYPE_STICK ? SETTING_DEADZONE : 0;
+ wxGraphicsPath path = gc->CreatePath();
+ path.AddCircle(VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2,
+ g->control_group->numeric_settings[deadzone_idx]->GetValue() *
+ VIS_BITMAP_SIZE / 2);
+ gc->SetBrush(*wxLIGHT_GREY_BRUSH);
+ gc->FillPath(path);
+ }
+
+ // raw dot
+ ControlState xx, yy;
+ xx = g->control_group->controls[3]->control_ref->State();
+ xx -= g->control_group->controls[2]->control_ref->State();
+ yy = g->control_group->controls[1]->control_ref->State();
+ yy -= g->control_group->controls[0]->control_ref->State();
+
+ gc->SetPen(*wxTRANSPARENT_PEN);
+ gc->SetBrush(*wxGREY_BRUSH);
+ DrawCoordinate(gc, xx, yy);
+
+ // adjusted dot
+ if (x != 0 || y != 0)
+ {
+ gc->SetBrush(*wxRED_BRUSH);
+ // XXX: The adjusted values flip the Y axis to be in the format
+ // the Wii expects. Should this be in WiimoteEmu.cpp instead?
+ DrawCoordinate(gc, x, -y);
+ }
+ }
+ break;
+
+ case GROUP_TYPE_FORCE:
+ {
+ ControlState raw_dot[3];
+ ControlState adj_dot[3];
+ const ControlState deadzone = g->control_group->numeric_settings[0]->GetValue();
+
+ // adjusted
+ ((ControllerEmu::Force*)g->control_group)->GetState(adj_dot);
+
+ // raw
+ for (unsigned int i = 0; i < 3; ++i)
+ {
+ raw_dot[i] = (g->control_group->controls[i * 2 + 1]->control_ref->State() -
+ g->control_group->controls[i * 2]->control_ref->State());
+ }
+
+ // deadzone rect for forward/backward visual
+ gc->SetPen(*wxTRANSPARENT_PEN);
+ gc->SetBrush(*wxLIGHT_GREY_BRUSH);
+ int deadzone_height = deadzone * VIS_BITMAP_SIZE;
+ DrawCenteredRectangle(gc, 0, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE, deadzone_height);
+
+#define LINE_HEIGHT 2
+ int line_y;
+
+ // raw forward/background line
+ gc->SetBrush(*wxGREY_BRUSH);
+ line_y = VIS_COORD(raw_dot[2]);
+ DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT);
+
+ // adjusted forward/background line
+ if (adj_dot[2] != 0.0)
+ {
+ gc->SetBrush(*wxRED_BRUSH);
+ line_y = VIS_COORD(adj_dot[2]);
+ DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT);
+ }
+
+#define DEADZONE_RECT_SIZE 32
+
+ // empty deadzone square
+ gc->SetPen(*wxLIGHT_GREY_PEN);
+ gc->SetBrush(*wxWHITE_BRUSH);
+ DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, DEADZONE_RECT_SIZE,
+ DEADZONE_RECT_SIZE);
+
+ // deadzone square
+ gc->SetPen(*wxTRANSPARENT_PEN);
+ gc->SetBrush(*wxLIGHT_GREY_BRUSH);
+ int dz_size = (deadzone * DEADZONE_RECT_SIZE);
+ DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, dz_size, dz_size);
+
+ // raw dot
+ gc->SetBrush(*wxGREY_BRUSH);
+ DrawCoordinate(gc, raw_dot[1], raw_dot[0]);
+
+ // adjusted dot
+ if (adj_dot[1] != 0 && adj_dot[0] != 0)
+ {
+ gc->SetBrush(*wxRED_BRUSH);
+ DrawCoordinate(gc, adj_dot[1], adj_dot[0]);
+ }
+ }
+ break;
+
+ case GROUP_TYPE_BUTTONS:
+ {
+ const unsigned int button_count = static_cast<unsigned int>(g->control_group->controls.size());
+ std::vector<unsigned int> bitmasks(button_count);
+
+ // draw the shit
+ gc->SetPen(*wxGREY_PEN);
+
+ for (unsigned int n = 0; n < button_count; ++n)
+ bitmasks[n] = (1 << n);
+
+ unsigned int buttons = 0;
+ ((ControllerEmu::Buttons*)g->control_group)->GetState(&buttons, bitmasks.data());
+
+ // Draw buttons in rows of 8
+ for (unsigned int row = 0; row < ceil((float)button_count / 8.0f); row++)
+ {
+ unsigned int buttons_to_draw = 8;
+ if ((button_count - row * 8) <= 8)
+ buttons_to_draw = button_count - row * 8;
+
+ for (unsigned int n = 0; n < buttons_to_draw; ++n)
+ {
+ DrawButton(bitmasks, buttons, n, gc, g, row, null_matrix);
+ }
+ }
+ }
+ break;
+
+ case GROUP_TYPE_TRIGGERS:
+ {
+ const unsigned int trigger_count = static_cast<unsigned int>(g->control_group->controls.size());
+ std::vector<ControlState> trigs(trigger_count);
+
+ // draw the shit
+ gc->SetPen(*wxGREY_PEN);
+
+ ControlState deadzone = g->control_group->numeric_settings[0]->GetValue();
+ ((ControllerEmu::Triggers*)g->control_group)->GetState(trigs.data());
+
+ for (unsigned int n = 0; n < trigger_count; ++n)
+ {
+ ControlState trig_r = g->control_group->controls[n]->control_ref->State();
+
+ // outline
+ gc->SetBrush(*wxWHITE_BRUSH);
+ gc->DrawRectangle(0, n * 12, 64, 14);
+
+ // raw
+ gc->SetBrush(*wxGREY_BRUSH);
+ gc->DrawRectangle(0, n * 12, trig_r * 64, 14);
+
+ // deadzone affected
+ gc->SetBrush(*wxRED_BRUSH);
+ gc->DrawRectangle(0, n * 12, trigs[n] * 64, 14);
+
+ // text
+ // We don't want the text to be scaled/zoomed
+ gc->SetTransform(null_matrix);
+ gc->DrawText(StrToWxStr(g->control_group->controls[n]->name), 3 * g->m_scale,
+ (n * 12 + 1) * g->m_scale);
+ gc->SetTransform(scale_matrix);
+ }
+
+ // deadzone box
+ gc->SetPen(*wxLIGHT_GREY_PEN);
+ gc->SetBrush(*wxTRANSPARENT_BRUSH);
+ gc->DrawRectangle(0, 0, deadzone * 64, trigger_count * 14);
+ }
+ break;
+
+ case GROUP_TYPE_MIXED_TRIGGERS:
+ {
+ const unsigned int trigger_count = ((unsigned int)(g->control_group->controls.size() / 2));
+
+ // draw the shit
+ gc->SetPen(*wxGREY_PEN);
+ ControlState thresh = g->control_group->numeric_settings[0]->GetValue();
+
+ for (unsigned int n = 0; n < trigger_count; ++n)
+ {
+ gc->SetBrush(*wxRED_BRUSH);
+
+ ControlState trig_d = g->control_group->controls[n]->control_ref->State();
+
+ ControlState trig_a =
+ trig_d > thresh ? 1 : g->control_group->controls[n + trigger_count]->control_ref->State();
+
+ gc->DrawRectangle(0, n * 12, 64 + 20, 14);
+ if (trig_d <= thresh)
+ gc->SetBrush(*wxWHITE_BRUSH);
+ gc->DrawRectangle(trig_a * 64, n * 12, 64 + 20, 14);
+ gc->DrawRectangle(64, n * 12, 32, 14);
+
+ // text
+ // We don't want the text to be scaled/zoomed
+ gc->SetTransform(null_matrix);
+ gc->DrawText(StrToWxStr(g->control_group->controls[n + trigger_count]->name), 3 * g->m_scale,
+ (n * 12 + 1) * g->m_scale);
+ gc->DrawText(StrToWxStr(std::string(1, g->control_group->controls[n]->name[0])),
+ (64 + 3) * g->m_scale, (n * 12 + 1) * g->m_scale);
+ gc->SetTransform(scale_matrix);
+ }
+
+ // threshold box
+ gc->SetPen(*wxLIGHT_GREY_PEN);
+ gc->SetBrush(*wxTRANSPARENT_BRUSH);
+ gc->DrawRectangle(thresh * 64, 0, 128, trigger_count * 14);
+ }
+ break;
+
+ case GROUP_TYPE_SLIDER:
+ {
+ const ControlState deadzone = g->control_group->numeric_settings[0]->GetValue();
+
+ ControlState state = g->control_group->controls[1]->control_ref->State() -
+ g->control_group->controls[0]->control_ref->State();
+ gc->SetPen(*wxTRANSPARENT_PEN);
+ gc->SetBrush(*wxGREY_BRUSH);
+ gc->DrawRectangle(31 + state * 30, 0, 2, 14);
+
+ ControlState adj_state;
+ ((ControllerEmu::Slider*)g->control_group)->GetState(&adj_state);
+ if (state)
+ {
+ gc->SetBrush(*wxRED_BRUSH);
+ gc->DrawRectangle(31 + adj_state * 30, 0, 2, 14);
+ }
+
+ // deadzone box
+ gc->SetPen(*wxLIGHT_GREY_PEN);
+ gc->SetBrush(*wxTRANSPARENT_BRUSH);
+ gc->DrawRectangle(32 - deadzone * 32, 0, deadzone * 64, 14);
+ }
+ break;
+
+ default:
+ break;
+ }
+ gc->SetTransform(null_matrix);
+}
+
+static void DrawBorder(wxGraphicsContext* gc, double scale)
+{
+ double pen_width = std::round(scale); // Pen width = 1px * scale
+
+ // Use the window caption bar color as a safe accent color.
+ wxPen border_pen(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION),
+ static_cast<int>(pen_width));
+ border_pen.SetCap(wxCAP_PROJECTING);
+
+ double width, height;
+ gc->GetSize(&width, &height);
+
+ wxGraphicsPath path = gc->CreatePath();
+ path.AddRectangle(pen_width / 2, pen_width / 2, width - pen_width, height - pen_width);
+ gc->SetPen(border_pen);
+ gc->StrokePath(path);
+}
+
+void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
+{
+ wxFont small_font(6, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
+ const wxColour font_color{0xB8B8B8};
+
+ g_controller_interface.UpdateInput();
+
+ wxMemoryDC dc;
+ auto lock = ControllerEmu::GetStateLock();
+ for (ControlGroupBox* g : control_groups)
+ {
+ // Only if this control group has a bitmap
+ if (!g->static_bitmap)
+ continue;
+
+ wxBitmap bitmap(g->static_bitmap->GetBitmap());
+ // NOTE: Selecting the bitmap inherits the bitmap's ScaleFactor onto the DC as well.
+ dc.SelectObjectAsSource(bitmap);
+ dc.SetBackground(*wxWHITE_BRUSH);
+ dc.Clear();
+
+#ifdef __WXGTK20__
+ int dc_height = 0;
+ dc.SetFont(small_font);
+ dc.GetTextExtent(g->control_group->name, nullptr, &dc_height);
+#endif
+
+ std::unique_ptr<wxGraphicsContext> gc{wxGraphicsContext::Create(dc)};
+ gc->DisableOffset();
+ gc->SetFont(small_font, font_color);
+
+#ifdef __WXGTK20__
+ double gc_height = 0;
+ gc->GetTextExtent(g->control_group->name, nullptr, &gc_height);
+ // On GTK2, wx creates a new empty Cairo/Pango context for the graphics context instead
+ // of reusing the wxMemoryDC one, this causes it to forget the screen DPI so fonts stop
+ // scaling, we need to scale it manually instead.
+ if (std::ceil(gc_height) < dc_height)
+ {
+ wxFont fixed_font(small_font);
+ fixed_font.SetPointSize(static_cast<int>(fixed_font.GetPointSize() * g->m_scale));
+ gc->SetFont(fixed_font, font_color);
+ }
+#endif
+
+ DrawControlGroupBox(gc.get(), g);
+ DrawBorder(gc.get(), g->m_scale);
+
+ // label for sticks and stuff
+ if (g->HasBitmapHeading())
+ gc->DrawText(StrToWxStr(g->control_group->name).Upper(), 4 * g->m_scale, 2 * g->m_scale);
+
+ gc.reset();
+ dc.SelectObject(wxNullBitmap);
+ g->static_bitmap->SetBitmap(bitmap);
+ }
+}