TableListBoxModel setBounds

I have a problem setting size and position of a TableListBoxModel. In this first screenshot, the Bottom TextButton shows where I want to place a TableListBoxModel:

This second screenshot shows a TableListBoxModel with exactly the same bounds, as specified by setBounds(), as the bottom TextButton in the first screenshot. The TableListBoxModel is too far down and too far to the right and is correspondingly truncated in both dimensions:

In this third screenshot, there are two TextButtons above the TableListBoxModel instead of just one. The vertical displacement and truncation of the TableListBoxModel is double compared with the second screenshot above:

The comments in this resize() code provide more detail on what seems to be happening:

void MainComponent::resized() {
  constexpr bool showTopmostButton = true;
  constexpr bool bottomComponentIsTableListBoxModel = true;

  constexpr int defaultHeight = 36;
  constexpr int defaultMargin = 30;
  const int defaultWidth = getWidth() - defaultMargin * 2;

  if (showTopmostButton) {
    topmostButton.setBounds(
      defaultMargin,
      defaultMargin,
      defaultWidth,
      defaultHeight);
  }
  topButton.setBounds(
    defaultMargin,
    showTopmostButton
      ? topmostButton.getBottom() + defaultMargin
      : defaultMargin,
    defaultWidth,
    defaultHeight);

  const int tableY = topButton.getBottom() + defaultMargin;
  // The table has to be tall to see any of it,
  // as its bottom will be truncated!
  constexpr int tableHeight = defaultHeight * 5;
  if (bottomComponentIsTableListBoxModel) {
    // If ONE button is shown above the TableListBoxModel,
    // there's a gap the height of a button plus a default margin
    // between the top of the table and where it should be,
    // which is a default margin below the button above.
    // And the bottom of the table is truncated.
    // Also, the table's left margin is double what it should be,
    // which is a default margin.
    // If TWO buttons are shown above the TableListBoxModel,
    // there's a gap twice the height of a button plus twp default
    // margins between the top of the table and where it should
    // be, which is a default margin below the button above.
    // And the bottom of the table is truncated even more
    // than when there is only one button above.
    // The table's left margin is out by the same amount
    // as when there is one button above.
    tableListBoxModel.setBounds(
      defaultMargin,
      tableY,
      defaultWidth,
      tableHeight);
  } else {
    // If the bottom component is a button instead of a
    // TableListBoxModel, there are no problems.
    bottomButton.setBounds(
      defaultMargin,
      tableY,
      defaultWidth,
      tableHeight);
  }
}
  • TableListBoxModel is not a component. Presumably your TableListBoxModel is also an instance of TableListBox.
  • Have you overridden resized in your TableListBox implementation? If so, what happens if you remove this override?

Thanks @reuk, you have solved my problem!

My ControllerTableComponent class did not derive from TableListBoxModel and TableListBox. Instead, following the TableListBox tutorial, it derived from TableListBoxModel and Component and contained a TableListBox member field which was positioned in the override of resized:

void ControllerTableComponent::resized() {
  table.setBounds(getBounds());
}

This works in the tutorial, where the table fills the window. It turns out to have been the problem when trying to make the table coexist with other components.

So I changed the table class to derived from TableListBox and TableListBoxModel, as you suggest, and got rid of the TableListBox field. In this context, a resize override makes no sense, as you imply. So I got rid of that too. Problem solved!


But I have just noticed that I now have a different problem: the paintcell override that provides the row contents is no longer working. I shall investigate further.

Yes, the row content is no longer shown because the paintCell override is no longer being executed. The paintRowBackground override is not executed either, though the getNumRows override is executed once for each row. I’d appreciate any ideas how I can fix this. Here’s the full code of the table class, starting with the header:

#pragma once
#include <JuceHeader.h>
using namespace juce;

class ControllerTableComponent final : public TableListBox,
                               public TableListBoxModel {
public:
  ControllerTableComponent();
  ~ControllerTableComponent() override;

  int getNumRows() override;
  void paintRowBackground(
    Graphics&, int rowNumber, int width, int height, bool rowIsSelected) override;
  void paintCell(
    Graphics& g, int rowNumber, int columnId, int width, int height, 
    bool rowIsSelected) override;
  void paint(Graphics&) override;

private:

  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ControllerTableComponent)
};

And here’s the cpp:

#include "ControllerTableComponent.h"

ControllerTableComponent::ControllerTableComponent() {

  getHeader().addColumn("Controller", 1, 75);
  getHeader().addColumn("Mapping", 2, 200);
}

ControllerTableComponent::~ControllerTableComponent() = default;

int ControllerTableComponent::getNumRows() {
  DBG("getNumRows");
  return 2;
}

void ControllerTableComponent::paintRowBackground(
  Graphics&, int rowNumber, int width, int height, bool rowIsSelected) {
  DBG("paintRowBackground");
}

void ControllerTableComponent::paintCell(
  Graphics& g, const int rowNumber, const int columnId, const int width,
  const int height, bool rowIsSelected) {
  DBG("paintCell");
  g.setColour(Colours::white);
  if (rowNumber == 0) {
    if (columnId == 1) {
      g.drawText("Slider 1", 2, 0, width, height,
                 Justification::centredLeft, true);
    } else if (columnId == 2) {
      g.drawText("Pitch", 2, 0, width, height,
                 Justification::centredLeft, true);
    }
  } else if (rowNumber == 1) {
    if (columnId == 1) {
      g.drawText("Slider 2", 2, 0, width, height,
                 Justification::centredLeft, true);
    } else if (columnId == 2) {
      g.drawText("Volume", 2, 0, width, height,
                 Justification::centredLeft, true);
    }
  }
}

void ControllerTableComponent::paint(Graphics& g) {
}

Looks like you’re missing a call to setModel, so that the TableListBox knows which Model to use.

Yes, I just realised that, thanks @reuk! Due to the wonders of dual inheritance from TableListBox and TableListBoxModel, it takes this somewhat bizarre form:

  setModel(this);

Just out of interest, I can’t see anything similar in the tutorial. So I can’t see how the TableListBox and TableListBoxModel know about each other in that context.