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
Comprehensive course covering advanced concepts, and how to use them on large-scale projects.
View Course