Skip to content

Commit

Permalink
libdeng2: Widget behavior flags and focus cycle
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed Jan 31, 2013
1 parent df58f07 commit 79e4c2b
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 6 deletions.
36 changes: 35 additions & 1 deletion doomsday/libdeng2/include/de/widgets/widget.h
Expand Up @@ -38,6 +38,20 @@ class DENG2_PUBLIC Widget
/// Widget that was expected to exist was not found. @ingroup errors
DENG2_ERROR(NotFoundError);

enum Behavior
{
/// Widget is invisible: not drawn. Hidden widgets also receive no events.
Hidden = 0x1,

/// Widget will only receive events if it has focus.
HandleEventsOnlyWhenFocused = 0x2,

DefaultBehavior = 0
};
Q_DECLARE_FLAGS(Behaviors, Behavior)

typedef QList<Widget *> Children;

public:
Widget(String const &name = "");
virtual ~Widget();
Expand All @@ -51,13 +65,33 @@ class DENG2_PUBLIC Widget
inline void hide() { show(false); }
void show(bool doShow = true);

/**
* Sets or clears one or more behavior flags.
*
* @param behavior Flags.
* @param set @c true to set, @c false to clear.
*/
void setBehavior(Behaviors behavior, bool set = true);

Behaviors behavior() const;

/**
* Sets the identifier of the widget that will receive focus when
* a focus navigation is requested.
*
* @param name Name of a widget.
*/
void setFocusNext(String const &name);

String focusNext() const;

// Tree organization.
void clear();
Widget &add(Widget *child);
Widget *remove(Widget &child);
Widget *find(String const &name);
Widget const *find(String const &name) const;
QList<Widget *> children() const;
Children children() const;
Widget *parent() const;

// Utilities.
Expand Down
51 changes: 46 additions & 5 deletions doomsday/libdeng2/src/widgets/widget.cpp
Expand Up @@ -29,14 +29,15 @@ struct Widget::Instance
Widget &self;
String name;
Widget *parent;
bool hidden;
Behaviors behavior;
String focusNext;

typedef QList<Widget *> Children;
typedef QMap<String, Widget *> NamedChildren;
Children children;
NamedChildren index;

Instance(Widget &w, String const &n) : self(w), name(n), parent(0), hidden(false)
Instance(Widget &w, String const &n) : self(w), name(n), parent(0)
{}

~Instance()
Expand Down Expand Up @@ -123,14 +124,41 @@ bool Widget::isHidden() const
{
for(Widget const *w = this; w != 0; w = w->d->parent)
{
if(w->d->hidden) return true;
if(w->d->behavior.testFlag(Hidden)) return true;
}
return false;
}

void Widget::show(bool doShow)
{
d->hidden = !doShow;
setBehavior(Hidden, !doShow);
}

void Widget::setBehavior(Behaviors behavior, bool set)
{
if(set)
{
d->behavior |= behavior;
}
else
{
d->behavior &= ~behavior;
}
}

Widget::Behaviors Widget::behavior() const
{
return d->behavior;
}

void Widget::setFocusNext(String const &name)
{
d->focusNext = name;
}

String Widget::focusNext() const
{
return d->focusNext;
}

void Widget::clear()
Expand Down Expand Up @@ -218,6 +246,19 @@ bool Widget::dispatchEvent(Event const *event, bool (Widget::*memberFunc)(Event
// Hidden widgets do not get events.
if(isHidden()) return false;

bool const thisHasFocus = (hasRoot() && root().focus() == this);

if(d->behavior.testFlag(HandleEventsOnlyWhenFocused) && !thisHasFocus)
{
return false;
}

if(thisHasFocus)
{
// The focused widget is offered events before dispatching to the tree.
return false;
}

// Tree is traversed in reverse order.
for(int i = d->children.size() - 1; i >= 0; --i)
{
Expand All @@ -236,7 +277,7 @@ bool Widget::dispatchEvent(Event const *event, bool (Widget::*memberFunc)(Event
return false;
}

QList<Widget *> Widget::children() const
Widget::Children Widget::children() const
{
return d->children;
}
Expand Down
13 changes: 13 additions & 0 deletions doomsday/libshell/src/textwidget.cpp
Expand Up @@ -126,13 +126,26 @@ bool TextWidget::handleEvent(Event const *event)
if(event->type() == Event::KeyPress)
{
DENG2_ASSERT(dynamic_cast<KeyEvent const *>(event) != 0);

KeyEvent const *keyEvent = static_cast<KeyEvent const *>(event);

foreach(Action *act, d->actions)
{
// Event will be used by actions.
if(act->tryTrigger(*keyEvent)) return true;
}

// Focus navigation.
if(keyEvent->key() == Qt::Key_Tab && hasFocus() && !focusNext().isEmpty())
{
Widget *next = root().find(focusNext());
if(next)
{
root().setFocus(next);
root().requestDraw();
return true;
}
}
}

return Widget::handleEvent(event);
Expand Down

0 comments on commit 79e4c2b

Please sign in to comment.