Creating Inline Widgets for Listbox - cnjinhao/nana GitHub Wiki
An inline widget is a child of a specified widget. It is associated with the data which is represented in the other widget. The listbox
inline widgets are associated with the column of certain item.
The following example demonstrates the use of listbox
inline widgets to create 2 widgets that represent a column of a listbox
item.
using namespace nana;
//Creates a textbox and button
//textbox shows the value of the sub item
//button is used to delete the item.
class inline_widget : public listbox::inline_notifier_interface
{
private:
//Creates inline widget
//listbox calls this method to create the widget
//The position and size of widget can be ignored in this process
virtual void create(window wd) override
{
//Create listbox
txt_.create(wd);
txt_.events().click([this]
{
//Select the item when clicks the textbox
indicator_->selected(pos_);
});
txt_.events().mouse_move([this]
{
//Highlight the item when hovers the textbox
indicator_->hovered(pos_);
});
txt_.events().key_char([this](const arg_keyboard& arg)
{
if (arg.key == keyboard::enter)
{
//Modify the item when enter is pressed
arg.ignore = true;
indicator_->modify(pos_, txt_.caption());
}
});
//Or modify the item when typing
txt_.events().text_changed([this]()
{
indicator_->modify(pos_, txt_.caption());
});
//Create button
btn_.create(wd);
btn_.caption("Delete");
btn_.events().click([this]
{
//Delete the item when button is clicked
auto & lsbox = dynamic_cast<listbox&>(indicator_->host());
lsbox.erase(lsbox.at(pos_));
});
btn_.events().mouse_move([this]
{
//Highlight the item when hovers the button
indicator_->hovered(pos_);
});
}
//Activates the inline widget, bound to a certain item of the listbox
//The inline_indicator is an object to operate the listbox item,
//pos is an index object refers to the item of listbox
virtual void activate(inline_indicator& ind, index_type pos)
{
indicator_ = &ind;
pos_ = pos;
}
void notify_status(status_type status, bool status_on) override
{
//Sets focus for the textbox when the item is selected
if((status_type::selecting == status) && status_on)
txt_.focus();
}
//Sets the inline widget size
//dimension represents the max size can be set
//The coordinate of inline widget is a logical coordinate to the sub item of listbox
void resize(const size& dimension) override
{
auto sz = dimension;
sz.width -= (sz.width < 50 ? 0 : 50); //Check for avoiding underflow.
txt_.size(sz);
rectangle r(sz.width + 5, 0, 45, sz.height);
btn_.move(r);
}
//Sets the value of inline widget with the value of the sub item
virtual void set(const value_type& value)
{
//Avoid emitting text_changed to set caption again, otherwise it
//causes infinite recursion.
if(txt_.caption() != value)
txt_.caption(value);
}
//Determines whether to draw the value of sub item
//e.g, when the inline widgets covers the whole background of the sub item,
//it should return false to avoid listbox useless drawing
bool whether_to_draw() const override
{
return false;
}
private:
inline_indicator * indicator_{ nullptr };
index_type pos_;
textbox txt_;
button btn_;
};
Use this inline_widget
int main()
{
using namespace nana;
form fm;
listbox lsbox(fm, rectangle{ 10, 10, 300, 200 });
//Create two columns
lsbox.append_header("column 0");
lsbox.append_header("column 1");
//Then append items
lsbox.at(0).append({ "Hello0", "World0" });
lsbox.at(0).append({ "Hello1", "World1" });
lsbox.at(0).append({ "Hello2", "World2" });
lsbox.at(0).append({ "Hello3", "World3" });
//Create a new category
lsbox.append("Category 1");
//Append items for category 1
lsbox.at(1).append({ "Hello4", "World4" });
lsbox.at(1).append({ "Hello5", "World5" });
lsbox.at(1).append({ "Hello6", "World6" });
lsbox.at(1).append({ "Hello7", "World7" });
//Set the inline_widget, the first column of category 0, the second column of category 1
lsbox.at(0).inline_factory(0, pat::make_factory<inline_widget>());
lsbox.at(1).inline_factory(1, pat::make_factory<inline_widget>());
fm.show();
exec();
}
The Screenshot
whether_to_draw()
method determines whether the listbox
have to draw the text of the sub item. Another case of use of whether_to_draw
is to make UI good look.
The listbox
internally manages these inline_widget
objects, and each inline_widget
object is temporarily bound to an item. The listbox
creates at most the number of inline_widget
objects for the number of displaying items. If a listbox
has 1000 items but only displays 10 items on the screen due to its height, the listbox
only creates 10 instances of the inline_widget
s. When the listbox's scrollbar scrolls, these 10 inline_widget
s objects will be rebound to other items by calling the inline_notifier_interface::activate()
method. For these reason, the inline_widget
should not keep the data associated with certain listbox item.