Passing a widget's draw context into another class for drawing

Hello!

I have a few classes that inherit from a base class called “engine”. There might be FooEngine, BarEngine, etc. The base class, “engine”, doesn’t inherit or implement anything. It’s mostly empty with a few virtual functions that the base classes implement.

I have a transparent widget called “LCDDisplay”. It functions similar to … you guessed it! An LCD display! It looks something like this:

struct LCDDisplay : TransparentWidget
{
  MyModule *module;

  LCDDisplay()
  {
    box.size = Vec(LCD_DISPLAY_WIDTH, LCD_DISPLAY_HEIGHT);
  }

  void draw(const DrawArgs &args) override
  {
    const auto vg = args.vg;

What I’d like to do is pass the responsibility for painting the content of the LCD display to the active “engine”. Here’s a rough diagram:

But I’m not sure what I can pass from the LCDDisplay struct to the engine. When I try to pass DrawArgs, I get a compilation error. I’m just a bit lost here and could use some guidance!

DrawArgs is nested in Widget, I stumbled a few times over that: You must write Widget::DrawArgs if your struct does not inherit from Widget.

3 Likes

This worked!

Here’s the code, if anyone needs it:

My widget code looks like this:


struct LCDDisplay : TransparentWidget
{
  MyModule *module;

  LCDDisplay()
  {
    // LCD_DISPLAY_WIDTH and LCD_DISPLAY_HEIGHT are defined elsewhere
    box.size = Vec(LCD_DISPLAY_WIDTH, LCD_DISPLAY_HEIGHT);
  }

  void draw(const DrawArgs &args) override
  {
    const auto vg = args.vg;

    // Save the drawing context to restore later
    nvgSave(vg);

    if(module)
    {
      // Draw a rectangle that takes up the entire display for testing
      nvgBeginPath(vg);
      nvgRect(vg, 0, 0, LCD_DISPLAY_WIDTH, LCD_DISPLAY_HEIGHT);
      nvgFillColor(vg, nvgRGBA(120, 20, 20, 100));
      nvgFill(vg);

The LCDDisplay is added to my main module’s Widget like so:

    LCDDisplay *lcd_display = new LCDDisplay();
    lcd_display->module = module;
    lcd_display->box.pos = mm2px(Vec(LCD_DISPLAY_X, LCD_DISPLAY_Y));
    addChild(lcd_display);

So far, everything is pretty typical. Now I start to introduce the concept of an “engine”. There will only be one active engine. When an engine is active, it should be able to draw on the LCD screen. I created a base class called “Engine”:

  struct Engine
  {
    virtual void LCDDraw(const Widget::DrawArgs &args) = 0;
  };

Then I create engines that inherit from the Engine class. Here’s one example called “Foo”:

  struct Foo : Engine
  {
    // Here I override the base class's LCDDraw function
    void LCDDraw(const Widget::DrawArgs &args) override
    {
      const auto vg = args.vg;

      // Here I would draw cool stuff.  
      // But for brevity sake, I'm just drawing another rectangle.
      nvgBeginPath(vg);
      nvgRect(vg, 0, 0, LCD_DISPLAY_WIDTH, LCD_DISPLAY_HEIGHT - 20);
      nvgFillColor(vg, nvgRGBA(0, 20, 10, 100));
      nvgFill(vg);
    }

And finally, I have to call LCDDraw, which I do from within LCDDisplay::draw. (All of this code is repeated except for one additional line that I added at the end.)

struct LCDDisplay : TransparentWidget
{
  MyModule *module;

  LCDDisplay()
  {
    // LCD_DISPLAY_WIDTH and LCD_DISPLAY_HEIGHT are defined elsewhere
    box.size = Vec(LCD_DISPLAY_WIDTH, LCD_DISPLAY_HEIGHT);
  }

  void draw(const DrawArgs &args) override
  {
    const auto vg = args.vg;

    // Save the drawing context to restore later
    nvgSave(vg);

    if(module)
    {
      // Draw a rectangle that takes up the entire display for testing
      nvgBeginPath(vg);
      nvgRect(vg, 0, 0, LCD_DISPLAY_WIDTH, LCD_DISPLAY_HEIGHT);
      nvgFillColor(vg, nvgRGBA(120, 20, 20, 100));
      nvgFill(vg);

      //
      //
      //
      // Here is the new line of code...

      module->selected_track->engine->LCDDraw(args);
      
      //
      //
      //
      //

Hope this is helpful!

3 Likes