Skip to content

Commit

Permalink
UI|Home: Subheading for custom profiles; fixed issues with sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed Mar 27, 2016
1 parent 4522fb9 commit 70154d7
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 31 deletions.
7 changes: 6 additions & 1 deletion doomsday/apps/client/src/ui/dialogs/createprofiledialog.cpp
Expand Up @@ -42,6 +42,7 @@ DENG_GUI_PIMPL(CreateProfileDialog)
void checkValidProfileName()
{
bool valid = false;

String const entry = self.profileName();
if(!entry.isEmpty())
{
Expand All @@ -60,6 +61,10 @@ DENG_GUI_PIMPL(CreateProfileDialog)
}) == LoopContinue;
}
}

// A game must be selected, too.
if(!gameChoice->isValidSelection()) valid = false;

self.buttonWidget(Id1)->enable(valid);
}
};
Expand All @@ -79,7 +84,7 @@ CreateProfileDialog::CreateProfileDialog(String const &gameFamily)
form->add(d->gameChoice = new ChoiceWidget);
DoomsdayApp::games().forAll([this, &gameFamily] (Game &game)
{
if(game.family() == gameFamily)
if(game.isPlayable() && game.family() == gameFamily)
{
d->gameChoice->items() << new ChoiceItem(game.title(), game.id());
}
Expand Down
101 changes: 87 additions & 14 deletions doomsday/apps/client/src/ui/home/gamecolumnwidget.cpp
Expand Up @@ -82,6 +82,7 @@ DENG_GUI_PIMPL(GameColumnWidget)
DENG2_ASSERT(static_cast<GameProfile *>(obj) == profile);

profile = nullptr;
d->addOrRemoveSubheading();

auto &items = d->menu->items();
items.remove(items.find(*this)); // item deleted
Expand All @@ -96,6 +97,7 @@ DENG_GUI_PIMPL(GameColumnWidget)
HomeMenuWidget *menu;
ButtonWidget *newProfileButton;
int restoredSelected = -1;
bool gotSubheading = false;

Instance(Public *i,
String const &gameFamily,
Expand Down Expand Up @@ -168,6 +170,19 @@ DENG_GUI_PIMPL(GameColumnWidget)
return menu->itemWidget<GamePanelButtonWidget>(item);
}

int userProfileCount() const
{
int count = 0;
menu->items().forAll([this, &count] (ui::Item const &item) {
if(!item.semantics().testFlag(ui::Item::Separator)) {
auto const *profile = item.as<ProfileItem>().profile;
if(profile && profile->isUserCreated()) ++count;
}
return LoopContinue;
});
return count;
}

void addItemForProfile(GameProfile &profile)
{
auto const &games = DoomsdayApp::games();
Expand All @@ -176,6 +191,7 @@ DENG_GUI_PIMPL(GameColumnWidget)
if(games[profile.game()].family() == gameFamily)
{
menu->items() << new ProfileItem(this, profile);
addOrRemoveSubheading();
}
}
}
Expand All @@ -190,6 +206,29 @@ DENG_GUI_PIMPL(GameColumnWidget)
});
}

void addOrRemoveSubheading()
{
int const userCount = userProfileCount();

if(userCount > 0 && !gotSubheading)
{
gotSubheading = true;
menu->items() << new ui::Item(ui::Item::Separator, tr("Custom Profiles"));
}
else if(!userCount && gotSubheading)
{
for(dsize pos = 0; pos < menu->items().size(); ++pos)
{
if(menu->items().at(pos).semantics().testFlag(ui::Item::Separator))
{
menu->items().remove(pos);
break;
}
}
gotSubheading = false;
}
}

/**
* Populates the game items using the currently available game profiles.
*/
Expand All @@ -204,39 +243,60 @@ DENG_GUI_PIMPL(GameColumnWidget)
sortItems();
}

enum Section { BuiltIn, Subheading, Custom };

static Section itemSection(ui::Item const &item)
{
// The list is divided into three sections.
if(item.semantics().testFlag(ui::Item::Separator)) return Subheading;
return item.as<ProfileItem>().profile->isUserCreated()? Custom : BuiltIn;
}

void sortItems()
{
menu->items().sort([] (ui::Item const &a, ui::Item const &b)
{
GameProfile const &prof1 = *a.as<ProfileItem>().profile;
GameProfile const &prof2 = *b.as<ProfileItem>().profile;
Section const section1 = itemSection(a);
Section const section2 = itemSection(b);

// User-created profiles in the end.
if(prof1.isUserCreated() && !prof2.isUserCreated())
if(section1 < section2)
{
return false;
return true;
}
if(!prof1.isUserCreated() && prof2.isUserCreated())
if(section1 > section2)
{
return true;
return false;
}
if(prof1.isUserCreated() && prof2.isUserCreated())

GameProfile const &prof1 = *a.as<ProfileItem>().profile;
GameProfile const &prof2 = *b.as<ProfileItem>().profile;

if(section1 == Custom)
{
// Sorted alphabetically.
return prof1.name().compareWithoutCase(prof2.name()) < 0;
}

// Sort games by release date.
return a.as<ProfileItem>().game().releaseDate().year() <
b.as<ProfileItem>().game().releaseDate().year();
// Sort built-in games by release date.
int year = a.as<ProfileItem>().game().releaseDate().year() -
b.as<ProfileItem>().game().releaseDate().year();
if(!year)
{
// ...or identifier.
return prof1.game().compareWithoutCase(prof2.game()) < 0;
}
return year < 0;
});
}

void updateItems()
{
menu->items().forAll([] (ui::Item const &item)
{
item.as<ProfileItem>().update();
if(!item.semantics().testFlag(ui::Item::Separator))
{
item.as<ProfileItem>().update();
}
return LoopContinue;
});
menu->updateLayout();
Expand All @@ -263,6 +323,17 @@ DENG_GUI_PIMPL(GameColumnWidget)

GuiWidget *makeItemWidget(ui::Item const &item, GuiWidget const *)
{
if(item.semantics().testFlag(ui::Item::Separator))
{
auto *heading = LabelWidget::newWithText(tr("Custom Profiles"));
heading->setSizePolicy(ui::Filled, ui::Expand);
heading->setFont("heading");
heading->setTextColor("accent");
heading->setAlignment(ui::AlignLeft);
heading->margins().setLeftRight("");
return heading;
}

auto const *profileItem = &item.as<ProfileItem>();
auto *button = new GamePanelButtonWidget(*profileItem->profile, savedItems);

Expand All @@ -272,7 +343,7 @@ DENG_GUI_PIMPL(GameColumnWidget)
auto *popup = new PopupMenuWidget;
button->add(popup);
popup->setDeleteAfterDismissed(true);
popup->setAnchorAndOpeningDirection(button->rule(), ui::Down);
popup->setAnchorAndOpeningDirection(button->label().rule(), ui::Down);

// Items suitable for all types of profiles.
popup->items()
Expand Down Expand Up @@ -302,7 +373,7 @@ DENG_GUI_PIMPL(GameColumnWidget)
<< new ui::ActionItem(tr("Edit..."), new CallbackAction([this, button, profileItem] ()
{
auto *dlg = CreateProfileDialog::editProfile(gameFamily, *profileItem->profile);
dlg->setAnchorAndOpeningDirection(button->rule(), ui::Up);
dlg->setAnchorAndOpeningDirection(button->label().rule(), ui::Up);
dlg->setDeleteAfterDismissed(true);
if(dlg->exec(root()))
{
Expand All @@ -320,6 +391,8 @@ DENG_GUI_PIMPL(GameColumnWidget)

void updateItemWidget(GuiWidget &widget, ui::Item const &item)
{
if(item.semantics().testFlag(ui::Item::Separator)) return; // Ignore.

auto &drawer = widget.as<GamePanelButtonWidget>();
drawer.updateContent();

Expand Down
19 changes: 11 additions & 8 deletions doomsday/apps/client/src/ui/home/gamepanelbuttonwidget.cpp
Expand Up @@ -38,7 +38,6 @@ DENG_GUI_PIMPL(GamePanelButtonWidget)
, public ChildWidgetOrganizer::IFilter
{
GameProfile &gameProfile;
Game const &game;
SavedSessionListData const &savedItems;
SaveListWidget *saves;
PackagesButtonWidget *packagesButton;
Expand All @@ -48,7 +47,6 @@ DENG_GUI_PIMPL(GamePanelButtonWidget)
Instance(Public *i, GameProfile &profile, SavedSessionListData const &savedItems)
: Base(i)
, gameProfile(profile)
, game(DoomsdayApp::games()[profile.game()])
, savedItems(savedItems)
{
packagesButton = new PackagesButtonWidget;
Expand Down Expand Up @@ -92,14 +90,19 @@ DENG_GUI_PIMPL(GamePanelButtonWidget)
self.panel().open();
}

Game const &game() const
{
return DoomsdayApp::games()[gameProfile.game()];
}

void playButtonPressed()
{
BusyMode_FreezeGameForBusyMode();
//ClientWindow::main().taskBar().close();
// TODO: Emit a signal that hides the Home and closes the taskbar.

// Switch the game.
DoomsdayApp::app().changeGame(game, DD_ActivateGameWorker);
DoomsdayApp::app().changeGame(game(), DD_ActivateGameWorker);

if(saves->selectedPos() != ui::Data::InvalidPos)
{
Expand Down Expand Up @@ -158,7 +161,7 @@ DENG_GUI_PIMPL(GamePanelButtonWidget)

// Only saved sessions for this game are to be included.
auto const &item = data.at(pos).as<SavedSessionListData::SaveItem>();
return item.gameId() == game.id();
return item.gameId() == gameProfile.game();
}
};

Expand Down Expand Up @@ -186,10 +189,10 @@ void GamePanelButtonWidget::setSelected(bool selected)

void GamePanelButtonWidget::updateContent()
{
enable(d->game.isPlayable());
enable(d->game().isPlayable());

String meta = !d->gameProfile.isUserCreated()? String::number(d->game.releaseDate().year())
: d->game.title();
String meta = !d->gameProfile.isUserCreated()? String::number(d->game().releaseDate().year())
: d->game().title();

if(isSelected())
{
Expand All @@ -203,7 +206,7 @@ void GamePanelButtonWidget::updateContent()
.arg(d->saves->childCount())
.arg(d->saves->childCount() != 1? "s" : "");
}*/
else
else if(!d->gameProfile.isUserCreated())
{
meta = tr("Start new session");
}
Expand Down
17 changes: 12 additions & 5 deletions doomsday/apps/client/src/ui/home/multiplayercolumnwidget.cpp
Expand Up @@ -153,13 +153,20 @@ DENG_GUI_PIMPL(MultiplayerColumnWidget)
auto const &second = b.as<ServerListItem>();

// Primarily sort by number of players.
if(first.info().numPlayers > second.info().numPlayers)
if(first.info().numPlayers == second.info().numPlayers)
{
return true;
// Secondarily by game ID.
int cmp = qstrcmp(first .info().gameIdentityKey,
second.info().gameIdentityKey);
if(!cmp)
{
// Lastly by server name.
return qstricmp(first.info().name,
second.info().name) < 0;
}
return cmp < 0;
}
// Secondarily by game ID.
return qstrcmp(first .info().gameIdentityKey,
second.info().gameIdentityKey) < 0;
return first.info().numPlayers - second.info().numPlayers > 0;
});
}
}
Expand Down
8 changes: 5 additions & 3 deletions doomsday/apps/client/src/ui/widgets/homemenuwidget.cpp
Expand Up @@ -76,10 +76,12 @@ void HomeMenuWidget::setSelectedIndex(int index)
if(index >= 0 && index < childWidgets().size())
{
unselectAll();
d->selectedIndex = index;

HomeItemWidget &widget = childWidgets().at(index)->as<HomeItemWidget>();
widget.setSelected(true);
if(HomeItemWidget *widget = childWidgets().at(index)->maybeAs<HomeItemWidget>())
{
d->selectedIndex = index;
widget->setSelected(true);
}
}
}

Expand Down

0 comments on commit 70154d7

Please sign in to comment.