WX: HiDPI: InputConfigDiag

Slight redesign of Control Configuration sub-window since SL_LABELS can't be
used with DolphinSlider.
This commit is contained in:
EmptyChaos
2016-08-02 06:22:48 +00:00
parent 741dfce05e
commit ac404517a3
3 changed files with 486 additions and 324 deletions

View File

@ -2,6 +2,8 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cmath>
#include <cstring>
#include <memory>
#include <string>
@ -11,8 +13,10 @@
#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/InputConfigDiag.h"
@ -36,69 +40,64 @@ struct ShapePosition
};
// regular octagon
static void DrawOctagon(wxDC* dc, ShapePosition p)
static void DrawOctagon(wxGraphicsContext* gc, ShapePosition p)
{
const int vertices = 8;
static constexpr int vertices = 8;
double radius = p.max;
wxPoint point[vertices];
wxGraphicsPath path = gc->CreatePath();
double angle = 2.0 * M_PI / vertices;
for (int i = 0; i < vertices; i++)
{
double a = (angle * i);
double a = angle * i;
double x = radius * cos(a);
double y = radius * sin(a);
point[i].x = x;
point[i].y = y;
if (i == 0)
path.MoveToPoint(x, y);
else
path.AddLineToPoint(x, y);
}
path.CloseSubpath();
dc->DrawPolygon(vertices, point, p.offset, p.offset);
wxGraphicsMatrix matrix = gc->CreateMatrix();
matrix.Translate(p.offset, p.offset);
path.Transform(matrix);
gc->DrawPath(path);
}
// irregular dodecagon
static void DrawDodecagon(wxDC* dc, ShapePosition p)
static void DrawDodecagon(wxGraphicsContext* gc, ShapePosition p)
{
const int vertices = 12;
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();
wxPoint point[vertices];
point[0].x = p.dz;
point[0].y = p.max;
point[1].x = p.diag;
point[1].y = p.diag;
point[2].x = p.max;
point[2].y = p.dz;
wxGraphicsMatrix matrix = gc->CreateMatrix();
matrix.Translate(p.offset, p.offset);
path.Transform(matrix);
point[3].x = p.max;
point[3].y = -p.dz;
point[4].x = p.diag;
point[4].y = -p.diag;
point[5].x = p.dz;
point[5].y = -p.max;
point[6].x = -p.dz;
point[6].y = -p.max;
point[7].x = -p.diag;
point[7].y = -p.diag;
point[8].x = -p.max;
point[8].y = -p.dz;
point[9].x = -p.max;
point[9].y = p.dz;
point[10].x = -p.diag;
point[10].y = p.diag;
point[11].x = -p.dz;
point[11].y = p.max;
dc->DrawPolygon(vertices, point, p.offset, p.offset);
gc->DrawPath(path);
}
static void DrawCenteredRectangle(wxDC& dc, int x, int y, int w, int h)
static void DrawCenteredRectangle(wxGraphicsContext* gc, double x, double y, double w, double h)
{
x -= w / 2;
y -= h / 2;
dc.DrawRectangle(x, y, w, h);
gc->DrawRectangle(x, y, w, h);
}
#define VIS_BITMAP_SIZE 64
@ -108,37 +107,47 @@ static void DrawCenteredRectangle(wxDC& dc, int x, int y, int w, int h)
#define COORD_VIS_SIZE 4
static void DrawCoordinate(wxDC& dc, ControlState x, ControlState y)
static void DrawCoordinate(wxGraphicsContext* gc, ControlState x, ControlState y)
{
int xc = VIS_COORD(x);
int yc = VIS_COORD(y);
DrawCenteredRectangle(dc, xc, yc, COORD_VIS_SIZE, COORD_VIS_SIZE);
double xc = VIS_COORD(x);
double yc = VIS_COORD(y);
DrawCenteredRectangle(gc, xc, yc, COORD_VIS_SIZE, COORD_VIS_SIZE);
}
static void DrawButton(unsigned int* const bitmasks, unsigned int buttons, unsigned int n, wxDC& dc,
ControlGroupBox* g, unsigned int row)
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])
{
dc.SetBrush(*wxRED_BRUSH);
gc->SetBrush(*wxRED_BRUSH);
}
else
{
auto lock = ControllerEmu::GetStateLock();
unsigned char amt = 255 - g->control_group->controls[(row * 8) + n]->control_ref->State() * 128;
dc.SetBrush(wxBrush(wxColour(amt, amt, amt)));
gc->SetBrush(wxBrush(wxColour(amt, amt, amt)));
}
dc.DrawRectangle(n * 12, (row == 0) ? 0 : (row * 11), 14, 12);
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
dc.DrawText(StrToWxStr(std::string(1, (name[1] && name[1] < 'a') ? name[1] : name[0])),
n * 12 + 2, 1 + ((row == 0) ? 0 : (row * 11)));
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(wxDC& dc, ControlGroupBox* g)
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:
@ -165,29 +174,17 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g)
// ir cursor forward movement
if (GROUP_TYPE_CURSOR == g->control_group->type)
{
if (z)
{
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxRED_BRUSH);
}
else
{
dc.SetPen(*wxGREY_PEN);
dc.SetBrush(*wxGREY_BRUSH);
}
dc.DrawRectangle(0, 31 - z * 31, 64, 2);
gc->SetBrush(z ? *wxRED_BRUSH : *wxGREY_BRUSH);
wxGraphicsPath path = gc->CreatePath();
path.AddRectangle(0, 31 - z * 31, 64, 2);
gc->FillPath(path);
}
// input zone
dc.SetPen(*wxLIGHT_GREY_PEN);
dc.SetBrush(*wxWHITE_BRUSH);
gc->SetPen(*wxLIGHT_GREY_PEN);
if (GROUP_TYPE_STICK == g->control_group->type)
{
// outline and fill colors
wxBrush LightGrayBrush("#dddddd");
wxPen LightGrayPen("#bfbfbf");
dc.SetBrush(LightGrayBrush);
dc.SetPen(LightGrayPen);
gc->SetBrush(wxColour(0xDDDDDD)); // Light Gray
ShapePosition p;
p.box = 64;
@ -215,20 +212,25 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g)
}
if (octagon)
DrawOctagon(&dc, p);
DrawOctagon(gc, p);
else
DrawDodecagon(&dc, p);
DrawDodecagon(gc, p);
}
else
{
dc.DrawRectangle(16, 16, 32, 32);
gc->SetBrush(*wxWHITE_BRUSH);
gc->DrawRectangle(16, 16, 32, 32);
}
if (GROUP_TYPE_CURSOR != g->control_group->type)
{
// deadzone circle
dc.SetBrush(*wxLIGHT_GREY_BRUSH);
dc.DrawCircle(32, 32, g->control_group->numeric_settings[SETTING_DEADZONE]->GetValue() * 32);
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
@ -238,21 +240,21 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g)
yy = g->control_group->controls[1]->control_ref->State();
yy -= g->control_group->controls[0]->control_ref->State();
dc.SetPen(*wxGREY_PEN);
dc.SetBrush(*wxGREY_BRUSH);
DrawCoordinate(dc, xx, yy);
gc->SetPen(*wxTRANSPARENT_PEN);
gc->SetBrush(*wxGREY_BRUSH);
DrawCoordinate(gc, xx, yy);
// adjusted dot
if (x != 0 || y != 0)
{
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxRED_BRUSH);
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(dc, x, -y);
DrawCoordinate(gc, x, -y);
}
}
break;
case GROUP_TYPE_FORCE:
{
ControlState raw_dot[3];
@ -270,69 +272,67 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g)
}
// deadzone rect for forward/backward visual
dc.SetBrush(*wxLIGHT_GREY_BRUSH);
dc.SetPen(*wxLIGHT_GREY_PEN);
gc->SetPen(*wxTRANSPARENT_PEN);
gc->SetBrush(*wxLIGHT_GREY_BRUSH);
int deadzone_height = deadzone * VIS_BITMAP_SIZE;
DrawCenteredRectangle(dc, 0, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE, deadzone_height);
DrawCenteredRectangle(gc, 0, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE, deadzone_height);
#define LINE_HEIGHT 2
int line_y;
// raw forward/background line
dc.SetPen(*wxGREY_PEN);
dc.SetBrush(*wxGREY_BRUSH);
gc->SetBrush(*wxGREY_BRUSH);
line_y = VIS_COORD(raw_dot[2]);
DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT);
DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT);
// adjusted forward/background line
if (adj_dot[2] != 0.0)
{
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxRED_BRUSH);
gc->SetBrush(*wxRED_BRUSH);
line_y = VIS_COORD(adj_dot[2]);
DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT);
DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT);
}
#define DEADZONE_RECT_SIZE 32
// empty deadzone square
dc.SetBrush(*wxWHITE_BRUSH);
dc.SetPen(*wxLIGHT_GREY_PEN);
DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, DEADZONE_RECT_SIZE,
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
dc.SetBrush(*wxLIGHT_GREY_BRUSH);
gc->SetPen(*wxTRANSPARENT_PEN);
gc->SetBrush(*wxLIGHT_GREY_BRUSH);
int dz_size = (deadzone * DEADZONE_RECT_SIZE);
DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, dz_size, dz_size);
DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, dz_size, dz_size);
// raw dot
dc.SetPen(*wxGREY_PEN);
dc.SetBrush(*wxGREY_BRUSH);
DrawCoordinate(dc, raw_dot[1], raw_dot[0]);
gc->SetBrush(*wxGREY_BRUSH);
DrawCoordinate(gc, raw_dot[1], raw_dot[0]);
// adjusted dot
if (adj_dot[1] != 0 && adj_dot[0] != 0)
{
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxRED_BRUSH);
DrawCoordinate(dc, adj_dot[1], adj_dot[0]);
gc->SetBrush(*wxRED_BRUSH);
DrawCoordinate(gc, adj_dot[1], adj_dot[0]);
}
}
break;
case GROUP_TYPE_BUTTONS:
{
unsigned int button_count = ((unsigned int)g->control_group->controls.size());
const unsigned int button_count = static_cast<unsigned int>(g->control_group->controls.size());
std::vector<unsigned int> bitmasks(button_count);
// draw the shit
dc.SetPen(*wxGREY_PEN);
gc->SetPen(*wxGREY_PEN);
unsigned int* const bitmasks = new unsigned int[button_count];
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);
((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++)
@ -343,155 +343,200 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g)
for (unsigned int n = 0; n < buttons_to_draw; ++n)
{
DrawButton(bitmasks, buttons, n, dc, g, row);
DrawButton(bitmasks, buttons, n, gc, g, row, null_matrix);
}
}
delete[] bitmasks;
}
break;
case GROUP_TYPE_TRIGGERS:
{
const unsigned int trigger_count = ((unsigned int)(g->control_group->controls.size()));
const unsigned int trigger_count = static_cast<unsigned int>(g->control_group->controls.size());
std::vector<ControlState> trigs(trigger_count);
// draw the shit
dc.SetPen(*wxGREY_PEN);
ControlState deadzone = g->control_group->numeric_settings[0]->GetValue();
gc->SetPen(*wxGREY_PEN);
ControlState* const trigs = new ControlState[trigger_count];
((ControllerEmu::Triggers*)g->control_group)->GetState(trigs);
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
dc.SetPen(*wxGREY_PEN);
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(0, n * 12, 64, 14);
gc->SetBrush(*wxWHITE_BRUSH);
gc->DrawRectangle(0, n * 12, 64, 14);
// raw
dc.SetBrush(*wxGREY_BRUSH);
dc.DrawRectangle(0, n * 12, trig_r * 64, 14);
gc->SetBrush(*wxGREY_BRUSH);
gc->DrawRectangle(0, n * 12, trig_r * 64, 14);
// deadzone affected
dc.SetBrush(*wxRED_BRUSH);
dc.DrawRectangle(0, n * 12, trigs[n] * 64, 14);
gc->SetBrush(*wxRED_BRUSH);
gc->DrawRectangle(0, n * 12, trigs[n] * 64, 14);
// text
dc.DrawText(StrToWxStr(g->control_group->controls[n]->name), 3, n * 12 + 1);
// 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);
}
delete[] trigs;
// deadzone box
dc.SetPen(*wxLIGHT_GREY_PEN);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(0, 0, deadzone * 64, trigger_count * 14);
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
dc.SetPen(*wxGREY_PEN);
gc->SetPen(*wxGREY_PEN);
ControlState thresh = g->control_group->numeric_settings[0]->GetValue();
for (unsigned int n = 0; n < trigger_count; ++n)
{
dc.SetBrush(*wxRED_BRUSH);
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();
dc.DrawRectangle(0, n * 12, 64 + 20, 14);
gc->DrawRectangle(0, n * 12, 64 + 20, 14);
if (trig_d <= thresh)
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(trig_a * 64, n * 12, 64 + 20, 14);
dc.DrawRectangle(64, n * 12, 32, 14);
gc->SetBrush(*wxWHITE_BRUSH);
gc->DrawRectangle(trig_a * 64, n * 12, 64 + 20, 14);
gc->DrawRectangle(64, n * 12, 32, 14);
// text
dc.DrawText(StrToWxStr(g->control_group->controls[n + trigger_count]->name), 3, n * 12 + 1);
dc.DrawText(StrToWxStr(std::string(1, g->control_group->controls[n]->name[0])), 64 + 3,
n * 12 + 1);
// 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
dc.SetPen(*wxLIGHT_GREY_PEN);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(thresh * 64, 0, 128, trigger_count * 14);
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();
dc.SetPen(*wxGREY_PEN);
dc.SetBrush(*wxGREY_BRUSH);
dc.DrawRectangle(31 + state * 30, 0, 2, 14);
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)
{
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxRED_BRUSH);
dc.DrawRectangle(31 + adj_state * 30, 0, 2, 14);
gc->SetBrush(*wxRED_BRUSH);
gc->DrawRectangle(31 + adj_state * 30, 0, 2, 14);
}
// deadzone box
dc.SetPen(*wxLIGHT_GREY_PEN);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(32 - deadzone * 32, 0, deadzone * 64, 14);
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();
GamepadPage* const current_page =
(GamepadPage*)m_pad_notebook->GetPage(m_pad_notebook->GetSelection());
static_cast<GamepadPage*>(m_pad_notebook->GetPage(m_pad_notebook->GetSelection()));
wxMemoryDC dc;
auto lock = ControllerEmu::GetStateLock();
for (ControlGroupBox* g : current_page->control_groups)
{
// if this control group has a bitmap
if (g->static_bitmap)
// 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.SelectObject(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)
{
wxMemoryDC dc;
wxBitmap bitmap(g->static_bitmap->GetBitmap());
dc.SelectObject(bitmap);
dc.Clear();
dc.SetFont(small_font);
dc.SetTextForeground(0xC0C0C0);
DrawControlGroupBox(dc, g);
// box outline
// Windows XP color
dc.SetPen(wxPen("#7f9db9"));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(0, 0, bitmap.GetWidth(), bitmap.GetHeight());
// label for sticks and stuff
if (64 == bitmap.GetHeight())
dc.DrawText(StrToWxStr(g->control_group->name).Upper(), 4, 2);
dc.SelectObject(wxNullBitmap);
g->static_bitmap->SetBitmap(bitmap);
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);
}
}