When creating UI elements that need to maintain a consistent physical size across different DPI settings, we need to consider both scaling and positioning. Here's a comprehensive approach to DPI-aware UIÂ elements.
Let's create a simple button class that maintains physical size:
// UISystem.h
#include <SDL.h>
#include <string>
#include <functional>
float GetDPIScale(SDL_Window* Window) {/*...*/}
class Button {
SDL_Rect Bounds;
std::string Text;
// Width in screen coordinates
float PhysicalWidth;
// Height in screen coordinates
float PhysicalHeight;
public:
Button(
float X, float Y,
float Width, float Height,
const std::string& ButtonText)
: PhysicalWidth{Width}
, PhysicalHeight{Height}
, Text{ButtonText} {
Bounds.x = int(X);
Bounds.y = int(Y);
// Initial size without scaling
UpdateSize(1.0f);
}
void UpdateSize(float Scale) {
Bounds.w = int(PhysicalWidth * Scale);
Bounds.h = int(PhysicalHeight * Scale);
}
void Draw(SDL_Renderer* Renderer) {
// Draw button background
SDL_SetRenderDrawColor(
Renderer, 200, 200, 200, 255);
SDL_RenderFillRect(Renderer, &Bounds);
// Draw border
SDL_SetRenderDrawColor(
Renderer, 100, 100, 100, 255);
SDL_RenderDrawRect(Renderer, &Bounds);
}
bool Contains(int X, int Y) {
return X >= Bounds.x
&& X < Bounds.x + Bounds.w
&& Y >= Bounds.y
&& Y < Bounds.y + Bounds.h;
}
};
class UISystem {
std::vector<Button> Buttons;
float CurrentScale{1.0f};
public:
void AddButton(
float X, float Y,
float Width, float Height,
const std::string& Text) {
Buttons.emplace_back(
X, Y, Width, Height, Text);
Buttons.back().UpdateSize(CurrentScale);
}
void UpdateDPIScale(float NewScale) {
if (CurrentScale != NewScale) {
CurrentScale = NewScale;
for (auto& Button : Buttons) {
Button.UpdateSize(NewScale);
}
}
}
void Draw(SDL_Renderer* Renderer) {
for (auto& Button : Buttons) {
Button.Draw(Renderer);
}
}
};
Here's how to use this system in a real application:
// main.cpp
#include <SDL.h>
#include "UISystem.h"
int main() {
SDL_SetHint(
SDL_HINT_WINDOWS_DPI_SCALING, "1");
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* Window{
SDL_CreateWindow(
"UI Scaling Demo",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
800, 600,
SDL_WINDOW_ALLOW_HIGHDPI
)};
SDL_Renderer* Renderer{
SDL_CreateRenderer(
Window, -1, SDL_RENDERER_ACCELERATED)};
UISystem UI;
UI.AddButton(50, 50, 200, 60, "Click Me");
UI.AddButton(50, 150, 200, 60, "Options");
float Scale{GetDPIScale(Window)};
UI.UpdateDPIScale(Scale);
SDL_Event E;
bool Running{true};
while (Running) {
while (SDL_PollEvent(&E)) {
if (E.type == SDL_QUIT) Running = false;
if (E.type == SDL_WINDOWEVENT) {
if (
E.window.event == SDL_WINDOWEVENT_RESIZED
|| E.window.event == SDL_WINDOWEVENT_MOVED
) {
Scale = GetDPIScale(Window);
UI.UpdateDPIScale(Scale);
}
}
}
SDL_SetRenderDrawColor(
Renderer, 240, 240, 240, 255);
SDL_RenderClear(Renderer);
UI.Draw(Renderer);
SDL_RenderPresent(Renderer);
}
SDL_DestroyRenderer(Renderer);
SDL_DestroyWindow(Window);
SDL_Quit();
return 0;
}
The key points to remember when implementing DPI-aware UI elements are:
Answers to questions are automatically generated and may not have been reviewed.
Learn how to create SDL applications that look great on modern displays across different platforms