Skip to content

Commit

Permalink
UI|Client|ToggleWidget: Added ToggleWidget
Browse files Browse the repository at this point in the history
A simple on/off toggle based on a button. The on/off graphic from the
style is stored centrally by GuiRootWidget.

Uses a procedural image to draw the state of the toggle.
  • Loading branch information
skyjake committed Aug 10, 2013
1 parent f92c785 commit 8fbd317
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 2 deletions.
2 changes: 2 additions & 0 deletions doomsday/client/client.pro
Expand Up @@ -370,6 +370,7 @@ DENG_HEADERS += \
include/ui/widgets/scrollareawidget.h \
include/ui/widgets/styledlogsinkformatter.h \
include/ui/widgets/taskbarwidget.h \
include/ui/widgets/togglewidget.h \
include/ui/widgets/widgetactions.h \
include/ui/windowsystem.h \
include/ui/zonedebug.h \
Expand Down Expand Up @@ -673,6 +674,7 @@ SOURCES += \
src/ui/widgets/scrollareawidget.cpp \
src/ui/widgets/styledlogsinkformatter.cpp \
src/ui/widgets/taskbarwidget.cpp \
src/ui/widgets/togglewidget.cpp \
src/ui/widgets/widgetactions.cpp \
src/ui/windowsystem.cpp \
src/ui/zonedebug.cpp \
Expand Down
11 changes: 11 additions & 0 deletions doomsday/client/include/ui/widgets/buttonwidget.h
Expand Up @@ -43,6 +43,17 @@ class ButtonWidget : public LabelWidget
*/
DENG2_DEFINE_AUDIENCE(StateChange, void buttonStateChanged(ButtonWidget &button, State state))

/**
* Notified immediately before the button's action is to be triggered. Will
* occur regardless of whether an action has been set.
*/
DENG2_DEFINE_AUDIENCE(Press, void buttonPressed(ButtonWidget &button))

/**
* Notified when the button's action is triggered (could be before or after
* the action). Will not occur if no action has been defined for the
* button.
*/
DENG2_DEFINE_AUDIENCE(Triggered, void buttonActionTriggered(ButtonWidget &button))

public:
Expand Down
1 change: 1 addition & 0 deletions doomsday/client/include/ui/widgets/guirootwidget.h
Expand Up @@ -55,6 +55,7 @@ class GuiRootWidget : public de::RootWidget
de::Id roundCorners() const;
de::Id gradientFrame() const;
de::Id borderGlow() const;
de::Id toggleOnOff() const;

static de::GLShaderBank &shaders();

Expand Down
61 changes: 61 additions & 0 deletions doomsday/client/include/ui/widgets/togglewidget.h
@@ -0,0 +1,61 @@
/** @file togglewidget.h Toggle widget.
*
* @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_TOGGLEWIDGET_H
#define DENG_CLIENT_TOGGLEWIDGET_H

#include "buttonwidget.h"

/**
* Toggle is a specialized button that maintains an on/off state in addition to
* the state of a ButtonWidget.
*/
class ToggleWidget : public ButtonWidget
{
public:
enum ToggleState {
Active,
Inactive
};

/**
* Audience to be notified whenever the toggle is toggled.
*/
DENG2_DEFINE_AUDIENCE(Toggle, void toggleStateChanged(ToggleWidget &toggle))

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

/**
* Sets the toggle state of the widget.
*/
void setToggleState(ToggleState state, bool notify = true);

void setActive(bool activate) { setToggleState(activate? Active : Inactive); }
void setInactive(bool deactivate) { setToggleState(deactivate? Inactive : Active ); }

ToggleState toggleState() const;

bool isActive() const { return toggleState() == Active; }
bool isInactive() const { return toggleState() == Inactive; }

private:
DENG2_PRIVATE(d)
};

#endif // DENG_CLIENT_TOGGLEWIDGET_H
15 changes: 13 additions & 2 deletions doomsday/client/src/ui/widgets/guirootwidget.cpp
Expand Up @@ -39,6 +39,7 @@ DENG2_PIMPL(GuiRootWidget)
Id roundCorners;
Id gradientFrame;
Id borderGlow;
Id toggleOnOff;
bool noFramesDrawnYet;

Instance(Public *i, ClientWindow *win)
Expand All @@ -61,6 +62,8 @@ DENG2_PIMPL(GuiRootWidget)
{
if(atlas.isNull())
{
Style const &st = ClientApp::windowSystem().style();

atlas.reset(AtlasTexture::newWithRowAllocator(
Atlas::BackingStore | Atlas::AllowDefragment,
GLTexture::maximumSize().min(GLTexture::Size(4096, 4096))));
Expand Down Expand Up @@ -105,8 +108,10 @@ DENG2_PIMPL(GuiRootWidget)
}

// Border glow.
borderGlow = atlas->alloc(ClientApp::windowSystem().style().images().
image("window.borderglow"));
borderGlow = atlas->alloc(st.images().image("window.borderglow"));

// On/Off toggle.
toggleOnOff = atlas->alloc(st.images().image("toggle.onoff"));
}
}
};
Expand Down Expand Up @@ -161,6 +166,12 @@ Id GuiRootWidget::borderGlow() const
return d->borderGlow;
}

Id GuiRootWidget::toggleOnOff() const
{
d->initAtlas();
return d->toggleOnOff;
}

GLShaderBank &GuiRootWidget::shaders()
{
return ClientApp::glShaderBank();
Expand Down
140 changes: 140 additions & 0 deletions doomsday/client/src/ui/widgets/togglewidget.cpp
@@ -0,0 +1,140 @@
/** @file togglewidget.cpp Toggle widget.
*
* @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/togglewidget.h"
#include "ui/widgets/proceduralimage.h"
#include "ui/widgets/guirootwidget.h"
#include "ui/style.h"
#include "clientapp.h"

#include <de/Animation>

using namespace de;

static TimeDelta const SWITCH_ANIM_SPAN = 0.3;

DENG2_PIMPL(ToggleWidget),
DENG2_OBSERVES(ButtonWidget, Press)
{
struct ToggleProceduralImage : public ProceduralImage
{
ToggleProceduralImage(GuiWidget &owner)
: _owner(owner),
_pos(1, Animation::EaseBoth),
_animating(false)
{
setSize(style().images().image("toggle.onoff").size());
}

Style const &style() const { return _owner.style(); }
Atlas &atlas() const { return _owner.root().atlas(); }

void setState(ToggleState st)
{
_pos.setValue(st == Inactive? 1 : 0, SWITCH_ANIM_SPAN);
_animating = true;
}

void update()
{
if(_animating)
{
_owner.requestGeometry();
if(_pos.done()) _animating = false;
}
}

void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect)
{
ColorBank::Colorf const &textColor = style().colors().colorf("text");
ColorBank::Colorf const &accentColor = style().colors().colorf("accent");

// Background.
ColorBank::Colorf bgColor = style().colors().colorf("background");
bgColor.w = 1;
verts.makeQuad(rect, accentColor * .5f * _pos + bgColor * (1 - _pos),
atlas().imageRectf(_owner.root().solidWhitePixel()).middle());

Id onOff = _owner.root().toggleOnOff();

// The on/off background.
verts.makeQuad(rect, accentColor * _pos + textColor * (1 - _pos),
atlas().imageRectf(onOff));

// The flipper.
Rectanglef flip = Rectanglef::fromSize(rect.topLeft +
Vector2f(1 + de::round<int>((1 - _pos) * (size().x - size().y)), 1),
Vector2f(size().y, size().y) - Vector2ui(2, 2));
verts.makeQuad(flip, bgColor, atlas().imageRectf(_owner.root().solidWhitePixel()).middle());
}

private:
GuiWidget &_owner;
Animation _pos;
bool _animating;
};

ToggleState state;
ToggleProceduralImage *procImage;

Instance(Public *i)
: Base(i),
state(Inactive),
procImage(new ToggleProceduralImage(self))
{
self.setImage(procImage);

self.audienceForPress += this;
}

~Instance()
{
self.audienceForPress -= this;
}

void buttonPressed(ButtonWidget &)
{
// Toggle the state.
self.setActive(self.isInactive());
}
};

ToggleWidget::ToggleWidget(String const &name) : ButtonWidget(name), d(new Instance(this))
{
setTextAlignment(ui::AlignRight);
setTextLineAlignment(ui::AlignLeft);
}

void ToggleWidget::setToggleState(ToggleState state, bool notify)
{
if(d->state != state)
{
d->state = state;
d->procImage->setState(state);

if(notify)
{
DENG2_FOR_AUDIENCE(Toggle, i) i->toggleStateChanged(*this);
}
}
}

ToggleWidget::ToggleState ToggleWidget::toggleState() const
{
return d->state;
}

0 comments on commit 8fbd317

Please sign in to comment.