2
\$\begingroup\$

I am trying to create a ListBox where each ListItem is just some text. I can make it so if a ListItem goes off the ListBox's height then don't show it. This is just a simple if statement in my render function checking to see if the ListItem's position is inside the ListBox's rectangle.

I want to improve this by making the ListBox scrollable. So similar to the picture below, I should be able to partially see the ListItem if I start scrolling down until it's fully in view.

enter image description here

How would I go about doing this? I'm thinking I would need some Camera and only render things inside the Camera's rectangle. Though, I do not know how to begin this.

My current code just doesn't render the ListItem if it goes off the ListBox's rectangle:

class ListBox {
  private:
    std::vector<ListItem*> list_items;

  public:
    ListBox();
    ~ListBox();

    void update();
    void render();
}

void ListBox::render() {
  // Loop through list items here
  if(item->rectangle.h + item->rectangle.y <= this->rectangle.h + this->rectangle.y) {
   item->render();
  }
}

Without the if statement, it looks like this:

enter image description here

I want "Test: 8" and "Test: 9" to be hidden inside the white rectangle until I scroll down.

\$\endgroup\$
3
\$\begingroup\$

You need two checks, a scroll variable to move the items and enable clipping:

// set clipper
SDL_RenderSetClipRect(renderer, &this->rectangle);

//loop through items
for(auto item : list_items){
    if(
        ((item->rectangle.y - scroll.y) < (this->rectangle.y + this->rectangle.h))
     && ((item->rectangle.y + item->rectangle.h - scroll.y) > this->rectangle.y)
    ){
        item->render(-scroll);
    }
}

// disable clipper
SDL_RenderSetClipRect(renderer, NULL);

For 2D scrolling:

if(
    ((item->rectangle.y - scroll.y) < (this->rectangle.y + this->rectangle.h))
 && ((item->rectangle.y + item->rectangle.h - scroll.y) > this->rectangle.y)
 && ((item->rectangle.x - scroll.x) < (this->rectangle.x + this->rectangle.w))
 && ((item->rectangle.x + item->rectangle.w - scroll.x) > this->rectangle.x)
){
    item->render(-scroll);
}

Where the items render themselves using the offset parameter passed to the render() function.

By having the scroll value "backward" (negative) you can scroll to an item with

scroll.y = item->rectangle.y; 
scroll.x = item->rectangle.x; 

and is consistent with the way scroll bars are usually constructed. You may need to invert this depending on how your coordinate system and scroll bars work.

The items will need to calculate a temporary SDL_Rect by adding the (x,y) offset passed to their render function to their rectangle.

SDL_Rect newrect;
newrect = rectangle;
newrect.x += offset.x; // offset passed as parameter
newrect.y += offset.y; // offset passed as parameter
SDL_RenderCopy(renderer, texture, NULL, &newrect);
\$\endgroup\$
  • \$\begingroup\$ Thanks for the reply. My coordinate system should be standard (y going down is positive, x going right is positive, and top left of screen is 0,0). I'm a little confused as to what to with the -scroll value being passed to item->render(). My item->render() function only does the following: SDL_RenderCopy(renderer, texture, NULL, rectangle). I don't see how to use that variable to partially show the ListItem while scrolling up or down. \$\endgroup\$ – o.o Oct 29 '17 at 21:32
  • \$\begingroup\$ Updated answer with SDL clipper code \$\endgroup\$ – Stephane Hockenhull Oct 29 '17 at 21:48
  • \$\begingroup\$ Thanks again! The clipping worked and the last two items are hidden. However, I'm not fully understanding the last part in the if statement: item->rectangle.y + item->rectangle.h - scroll.y) > this->rectangle.y. From my understanding, it's checking if the scrollbar is inside the rectangle. Though, I think I'm wrong as when I try to scroll (adding/subtracting 1 from scroll.y), the ListItems just appear and disappear. I never see the hidden items. \$\endgroup\$ – o.o Oct 29 '17 at 22:42
  • \$\begingroup\$ Here's a gif of it happening: giant.gfycat.com/AnguishedEnchantingGentoopenguin.webm. The scroll.y initial position is the white rectangle's y position. \$\endgroup\$ – o.o Oct 29 '17 at 22:53
  • 1
    \$\begingroup\$ You also need to move the items up by calculating the newrect with the scroll value offset. What the if() does is make sure that we only try to render the items that are at least partially visible. Even if they are clipped there's a cost to the render call. Your gif shows the proper behaviour: the objects stop being rendered as they (should) scroll upward out of the box. But they're not moving yet so it looks odd. \$\endgroup\$ – Stephane Hockenhull Oct 29 '17 at 23:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.