Implementing a Modal Dialog in SDL UI

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

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.

Structuring SDL Programs

Discover how to organize SDL components using manager classes, inheritance, and polymorphism for cleaner code.

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Using Inheritance Without Polymorphism in SDL/C++
Do I *have* to use polymorphism if I use inheritance?
Removing UI Components from a std::vector in SDL/C++
How would I remove a component from the UI manager's std::vector?
The virtual Keyword and Polymorphism in C++
What does the virtual keyword do, and why might I need it for polymorphism in SDL UI components?
How C++ Calls the Correct Method with virtual Functions (Dynamic Dispatch)
How does the computer know whether to call Rectangle::Render() or GreenRectangle::Render() when using pointers?
Controlling Drawing Order for Overlapping UI Components in SDL
How is the drawing order determined if components overlap in this UI structure?
Importance of Event Handling Order for UI Components in SDL
Does the order I call HandleEvent() on children matter?
Implementing Resizable UI Components in SDL
What's the best way to handle resizable UI components in SDL?
Creating a Scrollable Container in SDL
How do I create a scrollable container for UI elements that exceed the window size?
Animated UI Transitions in SDL
How can I create animated transitions between different UI states or screens?
Ask Your Own Question
Temporarily unavailable while we roll out updates. Back in a few days!