Building a Modular UI System

Implementing a Modal Dialog in SDL UI

How would I implement a modal dialog box using this component hierarchy?

Abstract art representing computer programming

Implementing a modal dialog box in our SDL UI system involves creating a new component that overlays the main UI and captures all input events. Here's how you can create a basic modal dialog:

First, let's define a ModalDialog class:

#include <SDL.h>
#include <functional>
#include <string>

class ModalDialog : public Component {
private:
  std::string message;
  SDL_Rect bounds;
  bool isVisible{false};
  std::function<void()> onConfirm;
  std::function<void()> onCancel;

public:
  ModalDialog(const std::string& msg, int w,
              int h)
    : message{msg}, bounds{0, 0, w, h} {}

  void Show(
      std::function<void()> confirmCallback,
      std::function<void()> cancelCallback) {
    isVisible = true;
    onConfirm = confirmCallback;
    onCancel = cancelCallback;
  }

  void Hide() { isVisible = false; }

  void HandleEvent(
      const SDL_Event& e) override {
    if (!isVisible)
      return;

    if (e.type == SDL_MOUSEBUTTONDOWN) {
      int mouseX = e.button.x;
      int mouseY = e.button.y;

      if (mouseX >= bounds.x + 10 &&
          mouseX <= bounds.x + 90 &&
          mouseY >= bounds.y + bounds.h - 40 &&
          mouseY <= bounds.y + bounds.h - 10) {
        onConfirm();
        Hide();
      } else if (mouseX >=
                 bounds.x + bounds.w - 90 &&
                 mouseX <=
                 bounds.x + bounds.w - 10 &&
                 mouseY >=
                 bounds.y + bounds.h - 40 &&
                 mouseY <=
                 bounds.y + bounds.h - 10) {
        onCancel();
        Hide();
      }
    }
  }

  void Render(SDL_Surface* surface) override {
    if (!isVisible)
      return;

    // Center the dialog
    bounds.x = (surface->w - bounds.w) / 2;
    bounds.y = (surface->h - bounds.h) / 2;

    // Draw dialog background
    SDL_FillRect(surface, &bounds,
                 SDL_MapRGB(surface->format,
                            200, 200, 200));

    // Draw message
    // In a real implementation, you'd use
    // SDL_ttf for text rendering

    // Draw buttons
    SDL_Rect confirmButton = {
        bounds.x + 10, bounds.y + bounds.h - 40,
        80, 30};
    SDL_FillRect(
        surface, &confirmButton,
        SDL_MapRGB(surface->format, 0, 255, 0));

    SDL_Rect cancelButton = {
        bounds.x + bounds.w - 90,
        bounds.y + bounds.h - 40, 80, 30};
    SDL_FillRect(
        surface, &cancelButton,
        SDL_MapRGB(surface->format, 255, 0, 0));
  }

  bool IsVisible() const { return isVisible; }
};

Now, let's integrate this ModalDialog into our UI system:

#include <iostream>

class UI {
private:
  std::vector<std::unique_ptr<Component>>
  components;
  ModalDialog dialog{"Are you sure?", 300, 150};

public:
  UI() {
    components.push_back(
        std::make_unique<Button>(
            10, 10, 100, 50, "Show Dialog"));
    components.push_back(
        std::make_unique<ModalDialog>(dialog));
  }

  void HandleEvent(const SDL_Event& e) {
    if (dialog.IsVisible()) {
      dialog.HandleEvent(e);
    } else {
      for (auto& component : components) {
        component->HandleEvent(e);
      }
    }
  }

  void Render(SDL_Surface* surface) {
    for (auto& component : components) {
      component->Render(surface);
    }
    dialog.Render(surface);
  }

  void ShowDialog() {
    dialog.Show(
        []() { std::cout << "Confirmed!\n"; },
        []() { std::cout << "Cancelled!\n"; });
  }
};

int main() {
  // SDL initialization code...

  UI ui;
  SDL_Event event;
  bool quit = false;

  while (!quit) {
    while (SDL_PollEvent(&event)) {
      if (event.type == SDL_QUIT) {
        quit = true;
      } else if (event.type ==
                 SDL_MOUSEBUTTONDOWN) {
        if (event.button.x >= 10 &&
            event.button.x <= 110 &&
            event.button.y >= 10 &&
            event.button.y <= 60) {
          ui.ShowDialog();
        }
      }
      ui.HandleEvent(event);
    }

    // Clear the screen
    SDL_FillRect(surface, nullptr,
                 SDL_MapRGB(surface->format,
                            255, 255, 255));

    // Render the UI
    ui.Render(surface);

    SDL_UpdateWindowSurface(window);
  }

  // SDL cleanup code...
  return 0;
}

This implementation creates a modal dialog that appears when a button is clicked. The dialog captures all input events when it's visible, effectively making the rest of the UI non-interactive.

The ModalDialog class handles its own rendering and event processing. When it's visible, it draws itself centered on the screen and responds to mouse clicks on its buttons.

In a real-world application, you'd want to add more features to this basic implementation:

  • Proper text rendering using a library like SDL_ttf
  • The ability to pass different messages to the dialog
  • More flexible positioning and sizing options
  • Keyboard navigation support
  • A semi-transparent overlay to dim the background when the dialog is open

Remember to handle edge cases, such as window resizing while the dialog is open, and ensure that the dialog is always on top of other UI elements.

This Question is from the Lesson:

Building a Modular UI System

Learn how to create a flexible and extensible UI system using C++ and SDL, focusing on component hierarchy and polymorphism.

Answers to questions are automatically generated and may not have been reviewed.

This Question is from the Lesson:

Building a Modular UI System

Learn how to create a flexible and extensible UI system using C++ and SDL, focusing on component hierarchy and polymorphism.

sdl2-promo.jpg
Part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 46 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved