Skip to content

Commit

Permalink
Refactor|UI|Widgets|Home: Packages list actions managed via ui::Data
Browse files Browse the repository at this point in the history
The action buttons shown for each package are now created in a menu
based on the provided ui::Data. This allows using any number of
buttons for each item, plus all the flexibility that MenuWidget
provides.

Added [...] info buttons to the Packages dialogs and the sidebar.
  • Loading branch information
skyjake committed Jun 18, 2016
1 parent 3cd619f commit 18201d3
Show file tree
Hide file tree
Showing 15 changed files with 332 additions and 231 deletions.
3 changes: 2 additions & 1 deletion doomsday/apps/client/include/ui/widgets/homeitemwidget.h
Expand Up @@ -50,7 +50,8 @@ class HomeItemWidget : public de::GuiWidget, public de::IAssetGroup
de::LabelWidget &icon();
de::LabelWidget &label();

void addButton(de::ButtonWidget *button);
void addButton(de::GuiWidget *button);
de::GuiWidget &buttonWidget(int index) const;
void setKeepButtonsVisible(bool yes);

virtual void setSelected(bool selected);
Expand Down
7 changes: 7 additions & 0 deletions doomsday/apps/client/include/ui/widgets/homemenuwidget.h
Expand Up @@ -21,6 +21,8 @@

#include <de/MenuWidget>

class HomeItemWidget;

/**
* Menu for items in Home columns.
*
Expand Down Expand Up @@ -49,6 +51,11 @@ class HomeMenuWidget : public de::MenuWidget
*/
void setSelectedIndex(de::ui::DataPos index);

de::ui::Item const *interactedItem() const;
de::ui::Item const *actionItem() const;
void setInteractedItem(de::ui::Item const *menuItem,
de::ui::Item const *actionItem);

signals:
void selectedIndexChanged(int index);
void itemClicked(int index);
Expand Down
Expand Up @@ -19,6 +19,8 @@
#ifndef DENG_CLIENT_UI_PACKAGESBUTTONWIDGET_H
#define DENG_CLIENT_UI_PACKAGESBUTTONWIDGET_H

#include "ui/dialogs/packagesdialog.h"

#include <doomsday/GameProfiles>
#include <de/ButtonWidget>
#include <QStringList>
Expand All @@ -33,11 +35,12 @@ class PackagesButtonWidget : public de::ButtonWidget
public:
PackagesButtonWidget();

void setDialogTitle(de::String const &title);
void setGameProfile(GameProfile const &profile);

void setSetupCallback(std::function<void (PackagesDialog &dialog)> func);
void setNoneLabel(de::String const &noneLabel);
void setDialogTitle(de::String const &title);
void setPackages(de::StringList const &packageIds);

de::StringList packages() const;

signals:
Expand Down
39 changes: 25 additions & 14 deletions doomsday/apps/client/include/ui/widgets/packageswidget.h
Expand Up @@ -43,28 +43,27 @@ class PackagesWidget : public de::GuiWidget, public de::IPersistent
virtual bool isPackageHighlighted(de::String const &packageId) const = 0;
};

class IButtonHandler
{
public:
virtual ~IButtonHandler();
virtual void packageButtonClicked(de::ButtonWidget &button, de::String const &packageId) = 0;
};

public:
PackagesWidget(de::String const &name = "");

void setFilterEditorMinimumY(de::Rule const &minY);

void setPackageStatus(IPackageStatus const &packageStatus);
void setButtonHandler(IButtonHandler &buttonHandler);
void setButtonLabels(de::String const &buttonLabel,
de::String const &highlightedButtonLabel);
void setButtonImages(de::DotPath const &styleId,
de::DotPath const &highlightedStyleId);
void setActionButtonAlwaysShown(bool showActions);

/**
* Sets the data items that determine what kind of action buttons get created
* for the items of the list.
*
* @param actionItem Items for action buttons.
*/
void setActionItems(de::ui::Data const &actionItems);

de::ui::Data &actionItems();

void setActionsAlwaysShown(bool showActions);

void setColorTheme(ColorTheme unselectedItem, ColorTheme selectedItem,
ColorTheme loadedUnselectedItem, ColorTheme loadedSelectedItem);
ColorTheme unselectedItemHilit, ColorTheme selectedItemHilit);

void populate();
void updateItems();
Expand All @@ -76,6 +75,18 @@ class PackagesWidget : public de::GuiWidget, public de::IPersistent
*/
de::ui::Item const *itemForPackage(de::String const &packageId) const;

/**
* Returns the ID of the package whose action buttons were most recently interacted
* with.
*
* @return Package ID.
*/
de::String actionPackage() const;

de::GuiWidget *actionWidget() const;

de::ui::Item const *actionItem() const;

void scrollToPackage(de::String const &packageId) const;

de::LineEditWidget &searchTermsEditor();
Expand Down
61 changes: 35 additions & 26 deletions doomsday/apps/client/src/ui/dialogs/packagesdialog.cpp
Expand Up @@ -37,18 +37,19 @@
#include <de/SequentialLayout>
#include <de/SignalAction>
#include <de/ui/SubwidgetItem>
#include <de/ui/VariantActionItem>

using namespace de;

DENG_GUI_PIMPL(PackagesDialog)
, public ChildWidgetOrganizer::IWidgetFactory
, public PackagesWidget::IPackageStatus
, public PackagesWidget::IButtonHandler
, DENG2_OBSERVES(Widget, ChildAddition)
{
StringList requiredPackages; // loaded first, cannot be changed
StringList selectedPackages;
LabelWidget *nothingSelected;
ui::ListData actions;
HomeMenuWidget *menu;
PackagesWidget *browser;
LabelWidget *gameTitle;
Expand Down Expand Up @@ -163,7 +164,7 @@ DENG_GUI_PIMPL(PackagesDialog)

// Indicator that is only visible when no packages have been added to the profile.
nothingSelected = new LabelWidget;
nothingSelected->setText(_E(b) + tr("Nothing Selected"));
nothingSelected->setText(_E(b) + tr("No Packages Selected"));
nothingSelected->setFont("heading");
nothingSelected->setOpacity(0.5f);
nothingSelected->rule()
Expand Down Expand Up @@ -208,12 +209,38 @@ DENG_GUI_PIMPL(PackagesDialog)

// Package browser.
self.rightArea().add(browser = new PackagesWidget(self.name() + ".filter"));
browser->setActionButtonAlwaysShown(true);
browser->setActionsAlwaysShown(true);
browser->setPackageStatus(*this);
browser->setButtonHandler(*this);
//browser->setButtonLabels(tr("Add"), tr("Remove"));
browser->setButtonLabels("", "");
browser->setButtonImages("create", "close.ringless");

// Action for showing information about the package.
actions << new ui::SubwidgetItem(tr("..."), ui::Up, [this] () -> PopupWidget *
{
return new PackagePopupWidget(browser->actionPackage());
});

// Action for (de)selecting the package.
actions << new ui::VariantActionItem("create",
"close.ringless",
String(),
String(),
new CallbackAction([this] ()
{
String const packageId = browser->actionPackage();
if (!selectedPackages.contains(packageId))
{
selectedPackages.append(packageId);
menu->items() << new SelectedPackageItem(packageId);
updateNothingIndicator();
updateGameTitle();
}
else
{
removePackage(packageId);
}
browser->actionItem()->notifyChange();
}));
browser->setActionItems(actions);

//browser->setColorTheme(Normal, Normal, Normal, Normal);
browser->rule()
.setInput(Rule::Left, self.rightArea().contentRule().left())
Expand All @@ -223,11 +250,6 @@ DENG_GUI_PIMPL(PackagesDialog)
browser->setFilterEditorMinimumY(self.rightArea().rule().top());
}

~Instance()
{
menu->audienceForChildAddition() -= this;
}

void populate()
{
menu->items().clear();
Expand Down Expand Up @@ -288,27 +310,14 @@ DENG_GUI_PIMPL(PackagesDialog)
return selectedPackages.contains(packageId);
}

void packageButtonClicked(ButtonWidget &, String const &packageId) override
{
if (!selectedPackages.contains(packageId))
{
selectedPackages.append(packageId);
menu->items() << new SelectedPackageItem(packageId);
updateNothingIndicator();
}
else
{
removePackage(packageId);
}
}

void removePackage(String const &packageId)
{
selectedPackages.removeOne(packageId);
auto pos = menu->items().findData(packageId);
DENG2_ASSERT(pos != ui::Data::InvalidPos);
menu->items().remove(pos);
updateNothingIndicator();
updateGameTitle();
}

void widgetChildAdded(Widget &child)
Expand Down
2 changes: 1 addition & 1 deletion doomsday/apps/client/src/ui/home/gamecolumnwidget.cpp
Expand Up @@ -501,7 +501,7 @@ GameColumnWidget::GameColumnWidget(String const &gameFamily,
d->menu->rule().height() +
d->newProfileButton->rule().height());

header().title().setText(String(_E(s) "%1\n" _E(.)_E(w) "%2")
header().title().setText(String(_E(s)_E(C) "%1\n" _E(.)_E(.)_E(w) "%2")
.arg( gameFamily == "DOOM"? "id Software" :
!gameFamily.isEmpty()? "Raven Software" : "")
.arg(!gameFamily.isEmpty()? QString(gameFamily)
Expand Down
12 changes: 12 additions & 0 deletions doomsday/apps/client/src/ui/home/gamepanelbuttonwidget.cpp
Expand Up @@ -57,6 +57,18 @@ DENG_GUI_PIMPL(GamePanelButtonWidget)
packagesButton = new PackagesButtonWidget;
packagesButton->setGameProfile(gameProfile);
packagesButton->setDialogTitle(profile.name());
packagesButton->setSetupCallback([this] (PackagesDialog &dialog)
{
// Add a button for starting the game.
dialog.buttons()
<< new DialogButtonItem(DialogWidget::Action,
style().images().image("play"),
tr("Play"),
new CallbackAction([this, &dialog] () {
dialog.accept();
playButtonPressed();
}));
});
self.addButton(packagesButton);

QObject::connect(packagesButton,
Expand Down
5 changes: 3 additions & 2 deletions doomsday/apps/client/src/ui/home/multiplayercolumnwidget.cpp
Expand Up @@ -233,8 +233,9 @@ MultiplayerColumnWidget::MultiplayerColumnWidget()
rule("gap") +
d->menu->rule().height());

header().title().setText(_E(s) "dengine.net\n" _E(.)_E(w) + tr("Multiplayer Games"));
header().info().setText(tr("Multiplayer servers are discovered via the dengine.net master server and broadcasting on the local network."));
header().title().setText(_E(s)_E(C) "dengine.net\n" _E(.)_E(.)_E(w) + tr("Multiplayer Games"));
header().info().setText(tr("Multiplayer servers are discovered via the dengine.net "
"master server and broadcasting on the local network."));
}

String MultiplayerColumnWidget::tabHeading() const
Expand Down
46 changes: 24 additions & 22 deletions doomsday/apps/client/src/ui/home/packagescolumnwidget.cpp
Expand Up @@ -19,26 +19,44 @@
#include "ui/home/packagescolumnwidget.h"
#include "ui/widgets/packageswidget.h"
#include "ui/widgets/packagepopupwidget.h"
#include "ui/widgets/homeitemwidget.h"

#include <de/PopupMenuWidget>
#include <de/CallbackAction>
#include <de/ui/ActionItem>
#include <de/ui/SubwidgetItem>

using namespace de;

DENG_GUI_PIMPL(PackagesColumnWidget)
, public PackagesWidget::IButtonHandler
{
PackagesWidget *packages;
LabelWidget *countLabel;
ui::ListData actions;

Instance(Public *i) : Base(i)
{
actions << new ui::SubwidgetItem(tr("..."), ui::Down, [this] () -> PopupWidget *
{
String const packageId = packages->actionPackage();

auto *popMenu = new PopupMenuWidget;
popMenu->setColorTheme(Inverted);
popMenu->items()
<< new ui::SubwidgetItem(tr("Info"), ui::Down,
[this, packageId] () -> PopupWidget * {
return new PackagePopupWidget(packageId);
})
<< new ui::Item(ui::Item::Separator)
<< new ui::ActionItem(style().images().image("close.ring"), tr("Uninstall..."));
return popMenu;
});

countLabel = new LabelWidget;

ScrollAreaWidget &area = self.scrollArea();
area.add(packages = new PackagesWidget("home-packages"));
packages->setButtonHandler(*this);
packages->setButtonLabels(tr("..."), tr("..."));
packages->setActionItems(actions);
packages->rule()
.setInput(Rule::Width, area.contentRule().width())
.setInput(Rule::Top, self.header().rule().bottom() +
Expand All @@ -58,23 +76,6 @@ DENG_GUI_PIMPL(PackagesColumnWidget)
}
});
}

void packageButtonClicked(ButtonWidget &button, de::String const &packageId) override
{
auto *popMenu = new PopupMenuWidget;
popMenu->setDeleteAfterDismissed(true);
popMenu->setColorTheme(Inverted);
popMenu->setAnchorAndOpeningDirection(button.rule(), ui::Down);
popMenu->items()
<< new ui::SubwidgetItem(tr("Info"), ui::Down,
[this, packageId] () -> PopupWidget * {
return new PackagePopupWidget(packageId);
})
<< new ui::Item(ui::Item::Separator)
<< new ui::ActionItem(style().images().image("close.ring"), tr("Uninstall..."));
root().addOnTop(popMenu);
popMenu->open();
}
};

PackagesColumnWidget::PackagesColumnWidget()
Expand All @@ -85,8 +86,6 @@ PackagesColumnWidget::PackagesColumnWidget()
header().info().setText(tr("Browse available packages and install new ones."));
header().infoPanel().close(0);

d->packages->setFilterEditorMinimumY(scrollArea().margins().top());

// Total number of packages listed.
d->countLabel->setFont("small");
d->countLabel->setSizePolicy(ui::Expand, ui::Fixed);
Expand All @@ -100,6 +99,9 @@ PackagesColumnWidget::PackagesColumnWidget()
header().rule().height() +
rule("gap") +
d->packages->rule().height());

d->packages->setFilterEditorMinimumY(scrollArea().margins().top());
d->packages->refreshPackages();
}

String PackagesColumnWidget::tabHeading() const
Expand Down

0 comments on commit 18201d3

Please sign in to comment.