Skip to content

Commit

Permalink
Refactor|UI|Ring Zero: Incorporate MP games in the game selection menu
Browse files Browse the repository at this point in the history
Added another subgroup into the Ring Zero game selection menu for
multiplayer games. MPSelectionWidget is a menu that populates
itself with the found MP servers.

Todo: The "Multiplayer Games" dialog should simply be a Games dialog
that shows the selection of games.
  • Loading branch information
skyjake committed Feb 2, 2014
1 parent dac63a3 commit e1e2107
Show file tree
Hide file tree
Showing 7 changed files with 528 additions and 318 deletions.
4 changes: 4 additions & 0 deletions doomsday/client/client.pro
Expand Up @@ -393,10 +393,12 @@ DENG_HEADERS += \
include/ui/widgets/cvarsliderwidget.h \
include/ui/widgets/cvartogglewidget.h \
include/ui/widgets/gameselectionwidget.h \
include/ui/widgets/gamesessionwidget.h \
include/ui/widgets/gameuiwidget.h \
include/ui/widgets/gamewidget.h \
include/ui/widgets/icvarwidget.h \
include/ui/widgets/keygrabberwidget.h \
include/ui/widgets/mpselectionwidget.h \
include/ui/widgets/multiplayermenuwidget.h \
include/ui/widgets/profilepickerwidget.h \
include/ui/widgets/taskbarwidget.h \
Expand Down Expand Up @@ -730,9 +732,11 @@ SOURCES += \
src/ui/widgets/cvarsliderwidget.cpp \
src/ui/widgets/cvartogglewidget.cpp \
src/ui/widgets/gameselectionwidget.cpp \
src/ui/widgets/gamesessionwidget.cpp \
src/ui/widgets/gamewidget.cpp \
src/ui/widgets/gameuiwidget.cpp \
src/ui/widgets/keygrabberwidget.cpp \
src/ui/widgets/mpselectionwidget.cpp \
src/ui/widgets/multiplayermenuwidget.cpp \
src/ui/widgets/profilepickerwidget.cpp \
src/ui/widgets/taskbarwidget.cpp \
Expand Down
45 changes: 45 additions & 0 deletions doomsday/client/include/ui/widgets/gamesessionwidget.h
@@ -0,0 +1,45 @@
/** @file gamesessionwidget.h
*
* @authors Copyright (c) 2014 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the GNU
* General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#ifndef DENG_CLIENT_GAMESESSIONWIDGET_H
#define DENG_CLIENT_GAMESESSIONWIDGET_H

#include <de/ButtonWidget>
#include <de/DocumentWidget>

/**
* Widget for representing an item (game session) in the game selection menu.
*
* It has two buttons: one for starting the game and one for configuring it.
*/
class GameSessionWidget : public de::GuiWidget
{
public:
GameSessionWidget();

de::ButtonWidget &loadButton();
de::ButtonWidget &infoButton();
de::DocumentWidget &document();

virtual void updateInfoContent();

private:
DENG2_PRIVATE(d)
};

#endif // DENG_CLIENT_GAMESESSIONWIDGET_H
38 changes: 38 additions & 0 deletions doomsday/client/include/ui/widgets/mpselectionwidget.h
@@ -0,0 +1,38 @@
/** @file mpselectionwidget.h
*
* @authors Copyright (c) 2014 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the GNU
* General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#ifndef DENG_CLIENT_MPSELECTIONWIDGET_H
#define DENG_CLIENT_MPSELECTIONWIDGET_H

#include <de/MenuWidget>

/**
* Menu that populates itself with available multiplayer games.
*
* @ingroup ui
*/
class MPSelectionWidget : public de::MenuWidget
{
public:
MPSelectionWidget();

private:
DENG2_PRIVATE(d)
};

#endif // DENG_CLIENT_MPSELECTIONWIDGET_H
231 changes: 2 additions & 229 deletions doomsday/client/src/ui/dialogs/multiplayerdialog.cpp
Expand Up @@ -17,246 +17,19 @@
*/

#include "ui/dialogs/multiplayerdialog.h"
#include "network/serverlink.h"
#include "clientapp.h"
#include "dd_main.h"
#include "con_main.h"
#include "CommandAction"
#include "ui/widgets/taskbarwidget.h"
#include "ui/dialogs/networksettingsdialog.h"

#include <de/charsymbols.h>
#include <de/SignalAction>
#include <de/SequentialLayout>
#include <de/ChildWidgetOrganizer>
#include <de/DocumentPopupWidget>
#include <de/ui/Item>

using namespace de;

DENG_GUI_PIMPL(MultiplayerDialog)
, DENG2_OBSERVES(ServerLink, DiscoveryUpdate)
, public ChildWidgetOrganizer::IWidgetFactory
{
static ServerLink &link() { return ClientApp::serverLink(); }
static String hostId(serverinfo_t const &sv) {
return String("%1:%2").arg(sv.address).arg(sv.port);
}

/**
* Data item with information about a found server.
*/
class ServerListItem : public ui::Item
{
public:
ServerListItem(serverinfo_t const &serverInfo)
{
setData(hostId(serverInfo));
std::memcpy(&_info, &serverInfo, sizeof(serverInfo));
}

serverinfo_t const &info() const
{
return _info;
}

void setInfo(serverinfo_t const &serverInfo)
{
std::memcpy(&_info, &serverInfo, sizeof(serverInfo));
notifyChange();
}

private:
serverinfo_t _info;
};

/**
* Widget representing a ServerListItem in the dialog's menu.
*/
struct ServerWidget : public GuiWidget
{
LabelWidget *title;
ButtonWidget *extra;
ButtonWidget *join;
QScopedPointer<SequentialLayout> layout;
DocumentPopupWidget *info;

struct JoinAction : public Action
{
public:
JoinAction(serverinfo_t const &sv, ButtonWidget *owner)
: _owner(owner)
{
_gameId = sv.gameIdentityKey;
_cmd = String("connect %1 %2").arg(sv.address).arg(sv.port);
}

void trigger()
{
Action::trigger();

BusyMode_FreezeGameForBusyMode();

// Closing the taskbar will cause this action to be deleted. Let's take
// ownership of the action so we can delete after we're done.
_owner->takeAction();

ClientWindow::main().taskBar().close();

App_ChangeGame(App_Games().byIdentityKey(_gameId), false /*no reload*/);
Con_Execute(CMDS_DDAY, _cmd.toLatin1(), false, false);

delete this;
}

Action *duplicate() const
{
DENG2_ASSERT(!"JoinAction: cannot duplicate");
return 0;
}

private:
ButtonWidget *_owner;
String _gameId;
String _cmd;
};

ServerWidget()
{
setBehavior(ContentClipping);

add(title = new LabelWidget);
add(extra = new ButtonWidget);
add(join = new ButtonWidget);

extra->setText(tr("..."));
join->setText(tr("Join"));

title->setSizePolicy(ui::Expand, ui::Expand);
title->setAppearanceAnimation(LabelWidget::AppearGrowVertically, 0.5);
title->setAlignment(ui::AlignTop);
title->setTextAlignment(ui::AlignRight);
title->setTextLineAlignment(ui::AlignLeft);
title->setImageAlignment(ui::AlignCenter);
title->setMaximumTextWidth(style().rules().rule("dialog.multiplayer.width").valuei());

extra->setSizePolicy(ui::Expand, ui::Expand);
join->setSizePolicy(ui::Expand, ui::Expand);

join->disable();

layout.reset(new SequentialLayout(rule().left(), rule().top(), ui::Right));
*layout << *title << *extra << *join;
rule().setSize(layout->width(), title->rule().height());

// Extra info popup.
info = new DocumentPopupWidget;
info->document().setMaximumLineWidth(style().rules().rule("dialog.multiplayer.width").valuei());
info->setAnchorAndOpeningDirection(extra->rule(), ui::Up);
add(info);

extra->setAction(new SignalAction(info, SLOT(open())));
}

void updateFromItem(ServerListItem const &item)
{
try
{
Game const &svGame = App_Games().byIdentityKey(item.info().gameIdentityKey);

if(style().images().has(svGame.logoImageId()))
{
title->setImage(style().images().image(svGame.logoImageId()));
}

serverinfo_t const &sv = item.info();

join->enable(sv.canJoin);
if(sv.canJoin)
{
join->setAction(new JoinAction(sv, join));
}

title->setText(String(_E(1) "%1 " _E(.)_E(2) "(%5/%6)" _E(.) "\n%2"
_E(D)_E(l) "\n%7 %4")
.arg(sv.name)
.arg(svGame.title())
.arg(sv.gameConfig)
.arg(sv.numPlayers)
.arg(sv.maxPlayers)
.arg(sv.map));

// Extra information.
info->document().setText(ServerInfo_AsStyledText(&sv));
}
catch(Error const &)
{
/// @todo
}
}
};

MenuWidget *list;
//MenuWidget *list;

Instance(Public *i) : Base(i)
{
self.area().add(list = new MenuWidget);

// Configure the server list widet.
list->setGridSize(1, ui::Expand, 0, ui::Expand);
list->organizer().setWidgetFactory(*this);

link().audienceForDiscoveryUpdate += this;
}

~Instance()
{
link().audienceForDiscoveryUpdate -= this;
}

GuiWidget *makeItemWidget(ui::Item const &item, GuiWidget const *)
{
ServerWidget *w = new ServerWidget;
// Automatically close the info popup if the dialog is closed.
QObject::connect(thisPublic, SIGNAL(closed()), w->info, SLOT(close()));
return w;
}

void updateItemWidget(GuiWidget &widget, ui::Item const &item)
{
widget.as<ServerWidget>().updateFromItem(item.as<ServerListItem>());
}

void linkDiscoveryUpdate(ServerLink const &link)
{
// Remove obsolete entries.
for(ui::Data::Pos idx = 0; idx < list->items().size(); ++idx)
{
String const id = list->items().at(idx).data().toString();
if(!link.isFound(Address::parse(id)))
{
list->items().remove(idx--);
}
}

// Add new entries and update existing ones.
foreach(de::Address const &host, link.foundServers())
{
serverinfo_t info;
if(!link.foundServerInfo(host, &info)) continue;

ui::Data::Pos found = list->items().findData(hostId(info));
if(found == ui::Data::InvalidPos)
{
// Needs to be added.
list->items().append(new ServerListItem(info));
}
else
{
// Update the info.
list->items().at(found).as<ServerListItem>().setInfo(info);
}
}
}
};

Expand All @@ -271,7 +44,7 @@ MultiplayerDialog::MultiplayerDialog(String const &name)
layout.setGridSize(1, 0);
//layout.setColumnAlignment(0, ui::AlignRight);

layout << *lab << *d->list;
layout << *lab;// << *d->list;

area().setContentSize(layout.width(), layout.height());

Expand Down

0 comments on commit e1e2107

Please sign in to comment.