// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
/*
* Copyright 2012-2013 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the applicable version of the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of both the GNU Lesser General Public
* License version 3 along with this program. If not, see
*
*
* Authored by: Diego Sarmentero
* Manuel de la Pena
*
*/
#include "PaymentPreview.h"
#include
#include
#include "unity-shared/CoverArt.h"
#include "unity-shared/DashStyle.h"
#include "unity-shared/PreviewStyle.h"
namespace unity
{
namespace dash
{
namespace previews
{
namespace
{
nux::logging::Logger logger("unity.dash.previews.payment.preview");
const RawPixel CONTENT_DATA_CHILDREN_SPACE = 5_em;
const RawPixel CONTENT_DATA_PADDING = 10_em;
const RawPixel OVERLAY_LAYOUT_SPACE = 20_em;
const RawPixel HEADER_CHILDREN_SPACE = 10_em;
const RawPixel HEADER_MAX_SIZE = 76_em;
const RawPixel IMAGE_MIN_MAX_SIZE = 64_em;
const RawPixel HEADER_SPACE = 10_em;
const RawPixel LINK_MIN_WIDTH = 178_em;
const RawPixel LINK_MAX_HEIGHT = 34_em;
}
class OverlaySpinner : public unity::debug::Introspectable, public nux::View
{
NUX_DECLARE_OBJECT_TYPE(OverlaySpinner, nux::View);
public:
OverlaySpinner();
nux::Property scale;
void Draw(nux::GraphicsEngine& GfxContext, bool force_draw);
void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw);
protected:
// Introspectable methods
std::string GetName() const;
void AddProperties(debug::IntrospectionData&);
// Key navigation
virtual bool AcceptKeyNavFocus();
private:
bool OnFrameTimeout();
nux::ObjectPtr spin_;
glib::Source::UniquePtr frame_timeout_;
nux::Matrix4 rotate_;
float rotation_;
};
NUX_IMPLEMENT_OBJECT_TYPE(OverlaySpinner);
OverlaySpinner::OverlaySpinner()
: nux::View(NUX_TRACKER_LOCATION)
, scale(1.0)
, rotation_(0.0f)
{
spin_ = dash::Style::Instance().GetSearchSpinIcon(scale);
rotate_.Identity();
rotate_.Rotate_z(0.0);
scale.changed.connect([this] (double scale) {
spin_ = dash::Style::Instance().GetSearchSpinIcon(scale);
QueueDraw();
});
}
void OverlaySpinner::Draw(nux::GraphicsEngine& GfxContext, bool force_draw)
{
nux::Geometry const& geo = GetGeometry();
nux::TexCoordXForm texxform;
GfxContext.PushClippingRectangle(geo);
nux::GetPainter().PaintBackground(GfxContext, geo);
texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD);
texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT);
texxform.min_filter = nux::TEXFILTER_LINEAR;
texxform.mag_filter = nux::TEXFILTER_LINEAR;
unsigned int current_alpha_blend;
unsigned int current_src_blend_factor;
unsigned int current_dest_blend_factor;
GfxContext.GetRenderStates().GetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor);
GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
nux::Geometry spin_geo(geo.x + ((geo.width - spin_->GetWidth()) / 2),
geo.y + ((geo.height - spin_->GetHeight()) / 2),
spin_->GetWidth(),
spin_->GetHeight());
// Geometry (== Rect) uses integers which were rounded above,
// hence an extra 0.5 offset for odd sizes is needed
// because pure floating point is not being used.
int spin_offset_w = !(geo.width % 2) ? 0 : 1;
int spin_offset_h = !(geo.height % 2) ? 0 : 1;
nux::Matrix4 matrix_texture;
matrix_texture = nux::Matrix4::TRANSLATE(-spin_geo.x - (spin_geo.width + spin_offset_w) / 2.0f,
-spin_geo.y - (spin_geo.height + spin_offset_h) / 2.0f, 0) * matrix_texture;
matrix_texture = rotate_ * matrix_texture;
matrix_texture = nux::Matrix4::TRANSLATE(spin_geo.x + (spin_geo.width + spin_offset_w) / 2.0f,
spin_geo.y + (spin_geo.height + spin_offset_h) / 2.0f, 0) * matrix_texture;
GfxContext.SetModelViewMatrix(GfxContext.GetModelViewMatrix() * matrix_texture);
GfxContext.QRP_1Tex(spin_geo.x,
spin_geo.y,
spin_geo.width,
spin_geo.height,
spin_->GetDeviceTexture(),
texxform,
nux::color::White);
// revert to model view matrix stack
GfxContext.ApplyModelViewMatrix();
GfxContext.PopClippingRectangle();
GfxContext.GetRenderStates().SetBlend(current_alpha_blend, current_src_blend_factor, current_dest_blend_factor);
if (!frame_timeout_)
{
frame_timeout_.reset(new glib::Timeout(22, sigc::mem_fun(this, &OverlaySpinner::OnFrameTimeout)));
}
}
void OverlaySpinner::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw)
{
}
bool OverlaySpinner::OnFrameTimeout()
{
rotation_ += 0.1f;
if (rotation_ >= 360.0f)
rotation_ = 0.0f;
rotate_.Rotate_z(rotation_);
QueueDraw();
frame_timeout_.reset();
return false;
}
std::string OverlaySpinner::GetName() const
{
return "OverlaySpinner";
}
void OverlaySpinner::AddProperties(debug::IntrospectionData& introspection)
{
introspection.add(GetAbsoluteGeometry());
}
bool OverlaySpinner::AcceptKeyNavFocus()
{
return false;
}
PaymentPreview::PaymentPreview(dash::Preview::Ptr preview_model)
: Preview(preview_model)
, data_(nullptr)
, full_data_layout_(nullptr)
, content_data_layout_(nullptr)
, overlay_layout_(nullptr)
, header_layout_(nullptr)
, body_layout_(nullptr)
, footer_layout_(nullptr)
{}
std::string PaymentPreview::GetName() const
{
return "PaymentPreview";
}
void PaymentPreview::AddProperties(debug::IntrospectionData& introspection)
{
Preview::AddProperties(introspection);
}
nux::Layout* PaymentPreview::GetHeader()
{
nux::HLayout* header_data_layout = new nux::HLayout();
header_data_layout->SetSpaceBetweenChildren(HEADER_CHILDREN_SPACE.CP(scale));
header_data_layout->SetMaximumHeight(HEADER_MAX_SIZE.CP(scale));
header_data_layout->SetMinimumHeight(HEADER_MAX_SIZE.CP(scale));
image_ = new CoverArt();
image_->SetMinMaxSize(IMAGE_MIN_MAX_SIZE.CP(scale), IMAGE_MIN_MAX_SIZE.CP(scale));
AddChild(image_.GetPointer());
UpdateCoverArtImage(image_.GetPointer());
header_data_layout->AddView(image_.GetPointer(), 0);
header_data_layout->AddLayout(GetTitle(), 0);
header_data_layout->AddSpace(HEADER_SPACE.CP(scale), 1);
header_data_layout->AddLayout(GetPrice(), 0);
return header_data_layout;
}
nux::ObjectPtr PaymentPreview::CreateLink(dash::Preview::ActionPtr action)
{
previews::Style& style = dash::previews::Style::Instance();
nux::ObjectPtr link;
link = new ActionLink(action->id,
action->display_name, NUX_TRACKER_LOCATION);
link->font_hint.Set(style.payment_form_labels_font().c_str());
link->SetMinimumWidth(LINK_MIN_WIDTH.CP(scale));
link->SetMaximumHeight(LINK_MAX_HEIGHT.CP(scale));
return link;
}
nux::ObjectPtr PaymentPreview::CreateButton(dash::Preview::ActionPtr action)
{
previews::Style& style = dash::previews::Style::Instance();
nux::ObjectPtr button;
button = new ActionButton(action->id,
action->display_name, action->icon_hint,
NUX_TRACKER_LOCATION);
button->SetFont(style.action_font());
button->SetExtraHint(action->extra_text, style.action_extra_font());
button->SetMinimumWidth(LINK_MIN_WIDTH.CP(scale));
button->SetMaximumHeight(LINK_MAX_HEIGHT.CP(scale));
return button;
}
void PaymentPreview::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw)
{
nux::Geometry const& base = GetGeometry();
gfx_engine.PushClippingRectangle(base);
nux::GetPainter().PaintBackground(gfx_engine, base);
if (full_data_layout_)
{
unsigned int alpha, src, dest = 0;
gfx_engine.GetRenderStates().GetBlend(alpha, src, dest);
gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
details_bg_layer_->SetGeometry(full_data_layout_->GetGeometry());
nux::GetPainter().RenderSinglePaintLayer(gfx_engine, full_data_layout_->GetGeometry(), details_bg_layer_.get());
gfx_engine.GetRenderStates().SetBlend(alpha, src, dest);
}
gfx_engine.PopClippingRectangle();
}
void PaymentPreview::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw)
{
nux::Geometry const& base = GetGeometry();
gfx_engine.PushClippingRectangle(base);
if (!IsFullRedraw())
nux::GetPainter().PushLayer(gfx_engine, details_bg_layer_->GetGeometry(), details_bg_layer_.get());
unsigned int alpha, src, dest = 0;
gfx_engine.GetRenderStates().GetBlend(alpha, src, dest);
gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
if (GetCompositionLayout())
GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw);
gfx_engine.GetRenderStates().SetBlend(alpha, src, dest);
if (!IsFullRedraw())
nux::GetPainter().PopBackground();
gfx_engine.PopClippingRectangle();
}
void PaymentPreview::ShowOverlay(bool isShown)
{
if (!full_data_layout_)
return;
if (isShown)
{
full_data_layout_->SetActiveLayerN(1);
}
else
{
full_data_layout_->SetActiveLayerN(0);
}
QueueDraw();
}
void PaymentPreview::ShowOverlay()
{
ShowOverlay(true);
}
void PaymentPreview::HideOverlay()
{
ShowOverlay(false);
}
void PaymentPreview::SetupBackground()
{
details_bg_layer_.reset(dash::previews::Style::Instance().GetBackgroundLayer());
}
void PaymentPreview::SetupViews()
{
full_data_layout_ = new nux::LayeredLayout();
// layout to be used to show the info
content_data_layout_ = new nux::VLayout();
content_data_layout_->SetSpaceBetweenChildren(CONTENT_DATA_CHILDREN_SPACE.CP(scale));
content_data_layout_->SetPadding(CONTENT_DATA_PADDING.CP(scale), CONTENT_DATA_PADDING.CP(scale), 0, CONTENT_DATA_PADDING.CP(scale));
header_layout_ = GetHeader();
content_data_layout_->AddLayout(header_layout_.GetPointer(), 1);
body_layout_ = GetBody();
content_data_layout_->AddLayout(body_layout_.GetPointer(), 1);
footer_layout_ = GetFooter();
content_data_layout_->AddLayout(footer_layout_.GetPointer(), 1);
full_data_layout_->AddLayout(content_data_layout_.GetPointer());
// layout to draw an overlay
overlay_layout_ = new nux::VLayout();
calculating_ = new StaticCairoText(
"Performing purchase", true,
NUX_TRACKER_LOCATION);
OverlaySpinner* spinner_ = new OverlaySpinner();
overlay_layout_->AddSpace(OVERLAY_LAYOUT_SPACE.CP(scale), 1);
overlay_layout_->AddView(calculating_, 0, nux::MINOR_POSITION_CENTER);
overlay_layout_->AddView(spinner_, 1, nux::MINOR_POSITION_CENTER);
overlay_layout_->AddSpace(OVERLAY_LAYOUT_SPACE.CP(scale), 1);
scale.changed.connect([this, spinner_] (double scale) { spinner_->scale = scale; });
full_data_layout_->AddLayout(overlay_layout_.GetPointer());
UpdateScale(scale);
SetLayout(full_data_layout_.GetPointer());
}
void PaymentPreview::UpdateScale(double scale)
{
Preview::UpdateScale(scale);
if (calculating_)
calculating_->SetScale(scale);
if (content_data_layout_)
{
content_data_layout_->SetSpaceBetweenChildren(CONTENT_DATA_CHILDREN_SPACE.CP(scale));
content_data_layout_->SetPadding(CONTENT_DATA_PADDING.CP(scale), CONTENT_DATA_PADDING.CP(scale), 0, CONTENT_DATA_PADDING.CP(scale));
}
}
}
}
}