Skip to content

Commit

Permalink
UI|Client: Added DialogWidget
Browse files Browse the repository at this point in the history
Dialogs are popups with a scrollable content area and a row of
buttons (in a menu widget).
  • Loading branch information
skyjake committed Aug 17, 2013
1 parent 7bfbf63 commit ffafccc
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 0 deletions.
2 changes: 2 additions & 0 deletions doomsday/client/client.pro
Expand Up @@ -356,6 +356,7 @@ DENG_HEADERS += \
include/ui/widgets/contextwidgetorganizer.h \
include/ui/widgets/documentwidget.h \
include/ui/widgets/gameselectionwidget.h \
include/ui/widgets/dialogwidget.h \
include/ui/widgets/gltextcomposer.h \
include/ui/widgets/guirootwidget.h \
include/ui/widgets/guiwidget.h \
Expand Down Expand Up @@ -667,6 +668,7 @@ SOURCES += \
src/ui/widgets/contextwidgetorganizer.cpp \
src/ui/widgets/documentwidget.cpp \
src/ui/widgets/gameselectionwidget.cpp \
src/ui/widgets/dialogwidget.cpp \
src/ui/widgets/gltextcomposer.cpp \
src/ui/widgets/guirootwidget.cpp \
src/ui/widgets/guiwidget.cpp \
Expand Down
108 changes: 108 additions & 0 deletions doomsday/client/include/ui/widgets/dialogwidget.h
@@ -0,0 +1,108 @@
/** @file dialogwidget.h Popup dialog.
*
* @authors Copyright (c) 2013 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_DIALOGWIDGET_H
#define DENG_CLIENT_DIALOGWIDGET_H

#include "popupwidget.h"
#include "scrollareawidget.h"
#include "menuwidget.h"

class GuiRootWidget;

/**
* Popup dialog.
*
* The content area of a dialog is scrollable. A menu with buttons is placed in
* the bottom of the dialog, for the actions available to the user.
*
* The contents of the dialog should be placed under the scroll area returned
* by DialogWidget::content() and positioned in relation to its content rule.
* When the dialog is set up, one must define the size of the content scroll
* area (width and height rules).
*/
class DialogWidget : public PopupWidget
{
Q_OBJECT

public:
/**
* Modality of the dialog. By default, dialogs are modal, meaning that
* while they are open, no events can get past the dialog.
*/
enum Modality {
Modal,
Nonmodal
};

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

void setModality(Modality modality);

Modality modality() const;

ScrollAreaWidget &content();

MenuWidget &buttons();

/**
* Shows the dialog and blocks execution until the dialog is closed --
* another event loop is started for event processing. Call either accept()
* or reject() to dismiss the dialog.
*
* @param root Root where to execute the dialog.
*
* @return Result code.
*/
int exec(GuiRootWidget &root);

// Events.
bool handleEvent(de::Event const &event);

public slots:
void accept(int result = 1);
void reject(int result = 0);

signals:
void accepted(int result);
void rejected(int result);

protected:
void preparePopupForOpening();

/**
* Derived classes can override this to do additional tasks before
* execution of the dialog begins. DialogWidget::prepare() must be called
* from the overridding methods.
*/
virtual void prepare();

/**
* Handles any tasks needed when the dialog is closing.
* DialogWidget::finish() must be called from overridding methods.
*
* @param result Result code.
*/
virtual void finish(int result);

private:
DENG2_PRIVATE(d)
};

#endif // DENG_CLIENT_DIALOGWIDGET_H
181 changes: 181 additions & 0 deletions doomsday/client/src/ui/widgets/dialogwidget.cpp
@@ -0,0 +1,181 @@
/** @file dialogwidget.cpp Popup dialog.
*
* @authors Copyright (c) 2013 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>
*/

#include "ui/widgets/dialogwidget.h"
#include "ui/widgets/guirootwidget.h"
#include "dd_main.h"

#include <QEventLoop>

using namespace de;

DENG2_PIMPL(DialogWidget),
DENG2_OBSERVES(ContextWidgetOrganizer, WidgetCreation)
{
Modality modality;
ScrollAreaWidget *area;
MenuWidget *buttons;
QEventLoop subloop;

Instance(Public *i) : Base(i), modality(Modal)
{
area = new ScrollAreaWidget("area");
buttons = new MenuWidget("buttons");

buttons->organizer().audienceForWidgetCreation += this;

// The menu maintains its own width and height based on children.
// Set up one row with variable number of columns.
buttons->setGridSize(0, ui::Expand, 1, ui::Expand);

area->rule()
.setInput(Rule::Left, self.rule().left())
.setInput(Rule::Top, self.rule().top())
.setInput(Rule::Width, area->contentRule().width() + area->margin() * 2)
.setInput(Rule::Height, area->contentRule().height() + area->margin() * 2);

// Buttons below the area.
buttons->rule()
.setInput(Rule::Top, area->rule().bottom() - area->margin()) // overlap margins
.setInput(Rule::Right, self.rule().right());

// A blank container widget acts as the popup content parent.
GuiWidget *container = new GuiWidget("container");
container->rule()
.setInput(Rule::Width, OperatorRule::maximum(area->rule().width(), buttons->rule().width()))
.setInput(Rule::Height, area->rule().height() + buttons->rule().height() -
area->margin());
container->add(area);
container->add(buttons);
self.setContent(container);
}

void widgetCreatedForItem(GuiWidget &widget, ui::Item const &)
{
// Make sure all label-based widgets in the button area
// manage their own size.
if(widget.is<LabelWidget>())
{
widget.as<LabelWidget>().setSizePolicy(ui::Expand, ui::Expand);
}
}
};

DialogWidget::DialogWidget(String const &name)
: PopupWidget(name), d(new Instance(this))
{
setOpeningDirection(ui::NoDirection);

if(!App_GameLoaded()) // blurring is not yet compatible with game rendering
{
Background bg = background();
bg.type = Background::BlurredWithBorderGlow;
bg.solidFill = Vector4f(0, 0, 0, .65f);
set(bg);
}
}

void DialogWidget::setModality(DialogWidget::Modality modality)
{
d->modality = modality;
}

DialogWidget::Modality DialogWidget::modality() const
{
return d->modality;
}

ScrollAreaWidget &DialogWidget::content()
{
return *d->area;
}

MenuWidget &DialogWidget::buttons()
{
return *d->buttons;
}

int DialogWidget::exec(GuiRootWidget &root)
{
// The widget is added to the root temporarily (as top child).
DENG2_ASSERT(!hasRoot());
root.add(this);

prepare();

int result = d->subloop.exec();

finish(result);

return result;
}

bool DialogWidget::handleEvent(Event const &event)
{
if(d->modality == Modal)
{
// The event should already have been handled by the children.
return true;
}

return PopupWidget::handleEvent(event);
}

void DialogWidget::accept(int result)
{
if(d->subloop.isRunning())
{
d->subloop.exit(result);
emit accepted(result);
}
}

void DialogWidget::reject(int result)
{
if(d->subloop.isRunning())
{
d->subloop.exit(result);
emit rejected(result);
}
}

void DialogWidget::preparePopupForOpening()
{
PopupWidget::preparePopupForOpening();

// Redo the layout (items visible now).
d->buttons->updateLayout();
}

void DialogWidget::prepare()
{
// Center the dialog.
setAnchor(root().viewWidth() / 2, root().viewHeight() / 2);
d->buttons->updateLayout();

// Make sure the newly added widget knows the view size.
viewResized();
notifyTree(&Widget::viewResized);

open();
}

void DialogWidget::finish(int)
{
close();
}

0 comments on commit ffafccc

Please sign in to comment.