Skip to content

Commit

Permalink
UI|Client: Virtualized items for package lists
Browse files Browse the repository at this point in the history
There is typically a large number of packages available, so
virtualizing the list contents makes things much faster.

The package item heights are no longer animated so they'll work more
nicely with virtualization.
  • Loading branch information
skyjake committed Jun 5, 2016
1 parent ee82f9e commit 7015f1c
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 16 deletions.
11 changes: 10 additions & 1 deletion doomsday/apps/client/include/ui/widgets/homeitemwidget.h
Expand Up @@ -35,7 +35,15 @@ class HomeItemWidget : public de::GuiWidget, public de::IAssetGroup
Q_OBJECT

public:
HomeItemWidget(de::String const &name = "");
enum Flag
{
NonAnimatedHeight = 0,
AnimatedHeight = 0x1,
};
Q_DECLARE_FLAGS(Flags, Flag)

public:
HomeItemWidget(Flags flags = AnimatedHeight, de::String const &name = "");

de::AssetGroup &assets() override;

Expand Down Expand Up @@ -97,6 +105,7 @@ class HomeItemWidget : public de::GuiWidget, public de::IAssetGroup
DENG2_PRIVATE(d)
};

Q_DECLARE_OPERATORS_FOR_FLAGS(HomeItemWidget::Flags)
Q_DECLARE_OPERATORS_FOR_FLAGS(HomeItemWidget::LogoFlags)

#endif // DENG_CLIENT_UI_HOME_HOMEITEMWIDGET_H
3 changes: 3 additions & 0 deletions doomsday/apps/client/include/ui/widgets/packageswidget.h
Expand Up @@ -76,6 +76,9 @@ class PackagesWidget : public de::GuiWidget, public de::IPersistent

de::LineEditWidget &searchTermsEditor();

// Events.
void initialize();

// Implements IPersistent.
void operator >> (de::PersistentState &toState) const;
void operator << (de::PersistentState const &fromState);
Expand Down
6 changes: 3 additions & 3 deletions doomsday/apps/client/src/ui/home/gamepanelbuttonwidget.cpp
Expand Up @@ -152,14 +152,14 @@ DENG_GUI_PIMPL(GamePanelButtonWidget)

//- ChildWidgetOrganizer::IFilter ---------------------------------------------

bool isItemAccepted(ChildWidgetOrganizer const &,
ui::Data const &data, ui::Data::Pos pos) const
bool isItemAccepted(ChildWidgetOrganizer const &, ui::Data const &,
ui::Item const &it) const
{
// User-created profiles currently have no saves associated with them.
if (gameProfile.isUserCreated()) return false;

// Only saved sessions for this game are to be included.
auto const &item = data.at(pos).as<SavedSessionListData::SaveItem>();
auto const &item = it.as<SavedSessionListData::SaveItem>();
return item.gameId() == gameProfile.game();
}
};
Expand Down
2 changes: 1 addition & 1 deletion doomsday/apps/client/src/ui/home/panelbuttonwidget.cpp
Expand Up @@ -32,7 +32,7 @@ DENG_GUI_PIMPL(PanelButtonWidget)
};

PanelButtonWidget::PanelButtonWidget(String const &name)
: HomeItemWidget(name)
: HomeItemWidget(AnimatedHeight, name)
, d(new Instance(this))
{
setBehavior(Focusable);
Expand Down
18 changes: 13 additions & 5 deletions doomsday/apps/client/src/ui/widgets/homeitemwidget.cpp
Expand Up @@ -185,7 +185,7 @@ DENG_GUI_PIMPL(HomeItemWidget)
}
};

HomeItemWidget::HomeItemWidget(String const &name)
HomeItemWidget::HomeItemWidget(Flags flags, String const &name)
: GuiWidget(name)
, d(new Instance(this))
{
Expand All @@ -196,16 +196,24 @@ HomeItemWidget::HomeItemWidget(String const &name)
style().fonts().font("default").height() +
style().fonts().font("default").lineSpacing();

AutoRef<Rule> smoothHeight(new AnimationRule(d->label->rule().height(), 0.3));
AutoRef<Rule> height;
if(flags.testFlag(AnimatedHeight))
{
height.reset(new AnimationRule(d->label->rule().height(), 0.3));
}
else
{
height.reset(d->label->rule().height());
}

d->background->rule()
.setInput(Rule::Top, rule().top())
.setInput(Rule::Height, smoothHeight)
.setInput(Rule::Height, height)
.setInput(Rule::Left, d->icon->rule().right())
.setInput(Rule::Right, rule().right());

d->icon->rule()
.setSize(iconSize, smoothHeight)
.setSize(iconSize, height)
.setInput(Rule::Left, rule().left())
.setInput(Rule::Top, rule().top());
d->icon->set(Background(Background::BorderGlow,
Expand All @@ -218,7 +226,7 @@ HomeItemWidget::HomeItemWidget(String const &name)
d->label->margins().setRight(*d->labelRightMargin + rule("gap"));

// Use an animated height rule for smoother list layout behavior.
rule().setInput(Rule::Height, smoothHeight);
rule().setInput(Rule::Height, height);
}

AssetGroup &HomeItemWidget::assets()
Expand Down
34 changes: 28 additions & 6 deletions doomsday/apps/client/src/ui/widgets/packageswidget.cpp
Expand Up @@ -27,6 +27,7 @@
#include <de/DocumentPopupWidget>
#include <de/FileSystem>
#include <de/LineEditWidget>
#include <de/Loop>
#include <de/MenuWidget>
#include <de/PackageLoader>
#include <de/PopupButtonWidget>
Expand Down Expand Up @@ -82,6 +83,7 @@ DENG_GUI_PIMPL(PackagesWidget)
, public ChildWidgetOrganizer::IFilter
, public ChildWidgetOrganizer::IWidgetFactory
{
LoopCallback mainCall;
LineEditWidget *search;
ButtonWidget *clearSearch;
HomeMenuWidget *menu;
Expand Down Expand Up @@ -135,7 +137,8 @@ DENG_GUI_PIMPL(PackagesWidget)
{
public:
PackageListWidget(PackageItem const &item, PackagesWidget &owner)
: _owner(owner)
: HomeItemWidget(NonAnimatedHeight) // virtualized, so don't make things difficult
, _owner(owner)
, _item(&item)
{
icon().setImageFit(ui::FitToSize | ui::OriginalAspectRatio);
Expand Down Expand Up @@ -352,13 +355,21 @@ DENG_GUI_PIMPL(PackagesWidget)
.setInput(Rule::Top, search->rule().bottom());
menu->organizer().setWidgetFactory(*this);
menu->organizer().setFilter(*this);
menu->setVirtualizationEnabled(true, rule("gap").valuei()*2 + rule("unit").valuei() +
int(style().fonts().font("default").height().value()*3));

QObject::connect(search, &LineEditWidget::editorContentChanged,
[this] () { updateFilterTerms(); });
QObject::connect(search, &LineEditWidget::enterPressed,
[this] () { focusFirstListedPackge(); });
}

~Instance()
{
// Private instance deleted before child widgets.
menu->organizer().unsetFilter();
}

void populate()
{
StringList packages = App::packageLoader().findAllPackages();
Expand Down Expand Up @@ -405,8 +416,13 @@ DENG_GUI_PIMPL(PackagesWidget)

void updateFilterTerms()
{
/// @todo Parse quoted terms. -jk
setFilterTerms(search->text().strip().split(QRegExp("\\s"), QString::SkipEmptyParts));
// Refiltering will potentially alter the widget tree, so doing it during
// event handling is not a great idea.
mainCall.enqueue([this] ()
{
/// @todo Parse quoted terms. -jk
setFilterTerms(search->text().strip().split(QRegExp("\\s"), QString::SkipEmptyParts));
});
}

void setFilterTerms(QStringList const &terms)
Expand Down Expand Up @@ -438,10 +454,10 @@ DENG_GUI_PIMPL(PackagesWidget)
return true;
}

bool isItemAccepted(ChildWidgetOrganizer const &,
ui::Data const &data, ui::Data::Pos pos) const
bool isItemAccepted(ChildWidgetOrganizer const &, ui::Data const &,
ui::Item const &it) const
{
auto &item = data.at(pos).as<PackageItem>();
auto &item = it.as<PackageItem>();

// The terms are looked in:
// - title
Expand Down Expand Up @@ -569,6 +585,12 @@ LineEditWidget &PackagesWidget::searchTermsEditor()
return *d->search;
}

void PackagesWidget::initialize()
{
GuiWidget::initialize();
d->menu->organizer().setVisibleArea(root().viewTop(), root().viewBottom());
}

void PackagesWidget::operator >> (PersistentState &toState) const
{
if (name().isEmpty()) return;
Expand Down

0 comments on commit 7015f1c

Please sign in to comment.