diff options
51 files changed, 2741 insertions, 397 deletions
diff --git a/dash/FilterBasicButton.cpp b/dash/FilterBasicButton.cpp index dbc19b7c2..52a63f8aa 100644 --- a/dash/FilterBasicButton.cpp +++ b/dash/FilterBasicButton.cpp @@ -100,8 +100,7 @@ void FilterBasicButton::InitTheme() void FilterBasicButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) { - int font_size = -1; - Style::Instance().Button(cr, faked_state, label_, font_size, Alignment::CENTER, true); + Style::Instance().Button(cr, faked_state, label_, -1, Alignment::CENTER, true); } void FilterBasicButton::RedrawFocusOverlay(nux::Geometry const& geom, cairo_t* cr) diff --git a/dash/previews/ActionButton.cpp b/dash/previews/ActionButton.cpp index 92c6b833c..b65e4f438 100644 --- a/dash/previews/ActionButton.cpp +++ b/dash/previews/ActionButton.cpp @@ -31,6 +31,8 @@ namespace const int kMinButtonHeight = 36; const int kMinButtonWidth = 48; +const int icon_size = 24; + nux::logging::Logger logger("unity.dash.actionbutton"); } @@ -42,7 +44,6 @@ namespace dash ActionButton::ActionButton(std::string const& label, std::string const& icon_hint, NUX_FILE_LINE_DECL) : nux::AbstractButton(NUX_FILE_LINE_PARAM) , image_(nullptr) - , static_text_(nullptr) { SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptKeyNavFocusOnMouseEnter(false); @@ -97,16 +98,16 @@ void ActionButton::BuildLayout(std::string const& label, std::string const& icon image_ = NULL; } - if (icon_hint_.size()>0) + if (!icon_hint_.empty()) { - image_ = new IconTexture(icon_hint, 24); + image_ = new IconTexture(icon_hint, icon_size); image_->Reference(); image_->texture_updated.connect([&](nux::BaseTexture*) { BuildLayout(label_, icon_hint_); }); image_->SetInputEventSensitivity(false); - image_->SetMinMaxSize(24,24); + image_->SetMinMaxSize(icon_size, icon_size); } } @@ -115,16 +116,15 @@ void ActionButton::BuildLayout(std::string const& label, std::string const& icon label_ = label; if (static_text_) { - static_text_->UnReference(); + static_text_.Release(); static_text_ = NULL; } - if (label_.size()>0) + if (!label_.empty()) { static_text_ = new nux::StaticCairoText(label_, NUX_TRACKER_LOCATION); - if (font_hint_.size()>0) + if (!font_hint_.empty()) static_text_->SetFont(font_hint_); - static_text_->Reference(); static_text_->SetInputEventSensitivity(false); static_text_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_CENTRE); } @@ -139,7 +139,7 @@ void ActionButton::BuildLayout(std::string const& label, std::string const& icon if (image_) layout->AddView(image_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); if (static_text_) - layout->AddView(static_text_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); + layout->AddView(static_text_.GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT); layout->AddSpace(0,1); SetLayout(layout); @@ -149,8 +149,7 @@ void ActionButton::BuildLayout(std::string const& label, std::string const& icon void ActionButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) { - int font_size=-1; - Style::Instance().Button(cr, faked_state, "", font_size, Alignment::CENTER, true); + Style::Instance().Button(cr, faked_state, "", -1, Alignment::CENTER, true); } void ActionButton::RedrawFocusOverlay(nux::Geometry const& geom, cairo_t* cr) @@ -164,7 +163,7 @@ long ActionButton::ComputeContentSize() nux::Geometry const& geo = GetGeometry(); - if (cached_geometry_ != geo) + if (cached_geometry_ != geo && geo.width > 0 && geo.height > 0) { if (cr_prelight_) cr_prelight_->Invalidate(geo); if (cr_active_) cr_active_->Invalidate(geo); diff --git a/dash/previews/ActionButton.h b/dash/previews/ActionButton.h index eafbb0f5b..7c3e3cb32 100644 --- a/dash/previews/ActionButton.h +++ b/dash/previews/ActionButton.h @@ -45,7 +45,7 @@ public: ActionButton(std::string const& label, std::string const& icon_hint, NUX_FILE_LINE_PROTO); ~ActionButton(); - sigc::signal<void, nux::AbstractButton*> click; + sigc::signal<void, ActionButton*> click; void SetFont(std::string const& font_hint); @@ -80,7 +80,7 @@ private: std::string font_hint_; IconTexture* image_; - nux::StaticCairoText* static_text_; + nux::ObjectPtr<nux::StaticCairoText> static_text_; }; } // namespace dash diff --git a/dash/previews/ApplicationPreview.cpp b/dash/previews/ApplicationPreview.cpp index 3ce6f2ffa..74371117a 100644 --- a/dash/previews/ApplicationPreview.cpp +++ b/dash/previews/ApplicationPreview.cpp @@ -146,17 +146,31 @@ void ApplicationPreview::SetupViews() { dash::ApplicationPreview* app_preview_model = dynamic_cast<dash::ApplicationPreview*>(preview_model_.get()); if (!app_preview_model) + { + LOG_ERROR(logger) << "Could not derive application preview model from given parameter."; return; + } previews::Style& style = dash::previews::Style::Instance(); - nux::HLayout* image_data_layout = new nux::HLayout(); image_data_layout->SetSpaceBetweenChildren(style.GetPanelSplitWidth()); + ///////////////////// + // Image + std::string image_hint; + if (preview_model_->image.Get()) + { + glib::String tmp_icon(g_icon_to_string(preview_model_->image.Get())); + image_hint = tmp_icon.Str(); + } image_ = new CoverArt(); - image_->SetImage(preview_model_->image.Get().RawPtr() ? g_icon_to_string(preview_model_->image.Get().RawPtr()) : ""); + if (image_hint.empty()) + image_->GenerateImage(preview_model_->image_source_uri); + else + image_->SetImage(image_hint); image_->SetFont(style.no_preview_image_font()); + ///////////////////// ///////////////////// // App Data Panel @@ -176,13 +190,14 @@ void ApplicationPreview::SetupViews() app_icon_ = new IconTexture(app_preview_model->app_icon.Get().RawPtr() ? g_icon_to_string(app_preview_model->app_icon.Get().RawPtr()) : "", 72); app_icon_->SetMinimumSize(style.GetAppIconAreaWidth(), style.GetAppIconAreaWidth()); app_icon_->SetMaximumSize(style.GetAppIconAreaWidth(), style.GetAppIconAreaWidth()); - icon_layout->AddView(app_icon_, 0); + icon_layout->AddView(app_icon_.GetPointer(), 0); app_rating_ = new PreviewRatingsWidget(); - app_rating_->SetMinimumHeight(36); + app_rating_->SetMaximumHeight(style.GetRatingWidgetHeight()); + app_rating_->SetMinimumHeight(style.GetRatingWidgetHeight()); app_rating_->SetRating(app_preview_model->rating); app_rating_->SetReviews(app_preview_model->num_ratings); - icon_layout->AddView(app_rating_, 0); + icon_layout->AddView(app_rating_.GetPointer(), 0); ///////////////////// @@ -198,31 +213,41 @@ void ApplicationPreview::SetupViews() title_ = new nux::StaticCairoText(app_preview_model->title); title_->SetLines(-1); title_->SetFont(style.title_font().c_str()); + title_subtitle_layout_->AddView(title_.GetPointer(), 1); - subtitle_ = new nux::StaticCairoText(app_preview_model->subtitle); - subtitle_->SetFont(style.subtitle_size_font().c_str()); - subtitle_->SetLines(-1); + if (!app_preview_model->subtitle.Get().empty()) + { + subtitle_ = new nux::StaticCairoText(app_preview_model->subtitle); + subtitle_->SetFont(style.subtitle_size_font().c_str()); + subtitle_->SetLines(-1); + title_subtitle_layout_->AddView(subtitle_.GetPointer(), 1); + } nux::VLayout* app_updated_copywrite_layout = new nux::VLayout(); app_updated_copywrite_layout->SetSpaceBetweenChildren(8); - license_ = new nux::StaticCairoText(app_preview_model->license); - license_->SetFont(style.app_license_font().c_str()); - license_->SetLines(-1); - - last_update_ = new nux::StaticCairoText(_("Last Updated ") + app_preview_model->last_update.Get()); - last_update_->SetFont(style.app_last_update_font().c_str()); - - copywrite_ = new nux::StaticCairoText(app_preview_model->copyright); - copywrite_->SetFont(style.app_copywrite_font().c_str()); - copywrite_->SetLines(-1); - - title_subtitle_layout_->AddView(title_, 1); - title_subtitle_layout_->AddView(subtitle_, 1); - - app_updated_copywrite_layout->AddView(license_, 1); - app_updated_copywrite_layout->AddView(last_update_, 1); - app_updated_copywrite_layout->AddView(copywrite_, 1); + if (!app_preview_model->license.Get().empty()) + { + license_ = new nux::StaticCairoText(app_preview_model->license); + license_->SetFont(style.app_license_font().c_str()); + license_->SetLines(-1); + app_updated_copywrite_layout->AddView(license_.GetPointer(), 1); + } + + if (!app_preview_model->last_update.Get().empty()) + { + last_update_ = new nux::StaticCairoText(_("Last Updated ") + app_preview_model->last_update.Get()); + last_update_->SetFont(style.app_last_update_font().c_str()); + app_updated_copywrite_layout->AddView(last_update_.GetPointer(), 1); + } + + if (!app_preview_model->copyright.Get().empty()) + { + copywrite_ = new nux::StaticCairoText(app_preview_model->copyright); + copywrite_->SetFont(style.app_copywrite_font().c_str()); + copywrite_->SetLines(-1); + app_updated_copywrite_layout->AddView(copywrite_.GetPointer(), 1); + } app_data_layout->AddLayout(title_subtitle_layout_); app_data_layout->AddLayout(app_updated_copywrite_layout); @@ -243,16 +268,20 @@ void ApplicationPreview::SetupViews() app_info_layout->SetSpaceBetweenChildren(12); app_info->SetLayout(app_info_layout); - description_ = new nux::StaticCairoText(""); - description_->SetFont(style.description_font().c_str()); - description_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_TOP); - description_->SetLines(-20); - description_->SetLineSpacing(2); - description_->SetText(app_preview_model->description); - app_info_layout->AddView(description_); - if (preview_model_->GetInfoHints().size() > 0) + if (!preview_model_->description.Get().empty()) + { + description_ = new nux::StaticCairoText(""); + description_->SetFont(style.description_font().c_str()); + description_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_TOP); + description_->SetLines(-style.GetDescriptionLineCount()); + description_->SetLineSpacing(style.GetDescriptionLineSpacing()); + description_->SetText(app_preview_model->description); + app_info_layout->AddView(description_.GetPointer()); + } + + if (!preview_model_->GetInfoHints().empty()) { - PreviewInfoHintWidget* preview_info_hints = new PreviewInfoHintWidget(preview_model_, 24); + PreviewInfoHintWidget* preview_info_hints = new PreviewInfoHintWidget(preview_model_, style.GetInfoHintIconSizeWidth()); app_info_layout->AddView(preview_info_hints); } ///////////////////// @@ -269,7 +298,7 @@ void ApplicationPreview::SetupViews() full_data_layout_->AddLayout(actions_layout, 0); ///////////////////// - image_data_layout->AddView(image_, 0); + image_data_layout->AddView(image_.GetPointer(), 0); image_data_layout->AddLayout(full_data_layout_, 1); @@ -295,16 +324,16 @@ long ApplicationPreview::ComputeContentSize() int details_width = MAX(0, geo.width - geo_art.width - style.GetPanelSplitWidth() - style.GetDetailsLeftMargin() - style.GetDetailsRightMargin()); int top_app_info_max_width = details_width - style.GetAppIconAreaWidth() - style.GetSpaceBetweenIconAndDetails(); - title_->SetMaximumWidth(top_app_info_max_width); - subtitle_->SetMaximumWidth(top_app_info_max_width); - license_->SetMaximumWidth(top_app_info_max_width); - last_update_->SetMaximumWidth(top_app_info_max_width); - copywrite_->SetMaximumWidth(top_app_info_max_width); - description_->SetMaximumWidth(details_width); + if (title_) { title_->SetMaximumWidth(top_app_info_max_width); } + if (subtitle_) { subtitle_->SetMaximumWidth(top_app_info_max_width); } + if (license_) { license_->SetMaximumWidth(top_app_info_max_width); } + if (last_update_) { last_update_->SetMaximumWidth(top_app_info_max_width); } + if (copywrite_) { copywrite_->SetMaximumWidth(top_app_info_max_width); } + if (description_) { description_->SetMaximumWidth(details_width); } for (nux::AbstractButton* button : action_buttons_) { - button->SetMinMaxSize(MIN((details_width - style.GetSpaceBetweenActions()) / 2, style.GetActionButtonMaximumWidth()), style.GetActionButtonHeight()); + button->SetMinMaxSize(CLAMP((details_width - style.GetSpaceBetweenActions()) / 2, 0, style.GetActionButtonMaximumWidth()), style.GetActionButtonHeight()); } image_->ComputeContentSize(); diff --git a/dash/previews/ApplicationPreview.h b/dash/previews/ApplicationPreview.h index 825d87e7c..3ee3b2744 100644 --- a/dash/previews/ApplicationPreview.h +++ b/dash/previews/ApplicationPreview.h @@ -70,15 +70,15 @@ protected: nux::VLayout* full_data_layout_; nux::VLayout* title_subtitle_layout_; - CoverArt* image_; - IconTexture* app_icon_; - PreviewRatingsWidget* app_rating_; - nux::StaticCairoText* title_; - nux::StaticCairoText* subtitle_; - nux::StaticCairoText* license_; - nux::StaticCairoText* last_update_; - nux::StaticCairoText* copywrite_; - nux::StaticCairoText* description_; + nux::ObjectPtr<CoverArt> image_; + nux::ObjectPtr<IconTexture> app_icon_; + nux::ObjectPtr<PreviewRatingsWidget> app_rating_; + nux::ObjectPtr<nux::StaticCairoText> title_; + nux::ObjectPtr<nux::StaticCairoText> subtitle_; + nux::ObjectPtr<nux::StaticCairoText> license_; + nux::ObjectPtr<nux::StaticCairoText> last_update_; + nux::ObjectPtr<nux::StaticCairoText> copywrite_; + nux::ObjectPtr<nux::StaticCairoText> description_; typedef std::unique_ptr<nux::AbstractPaintLayer> LayerPtr; LayerPtr details_bg_layer_; diff --git a/dash/previews/CMakeLists.txt b/dash/previews/CMakeLists.txt index 5a7de40a1..c01f6abc9 100644 --- a/dash/previews/CMakeLists.txt +++ b/dash/previews/CMakeLists.txt @@ -58,4 +58,18 @@ target_link_libraries (app_previews previews-lib unity-shared) # add_executable (music_previews StandaloneMusicPreview.cpp) add_dependencies (music_previews previews-lib) -target_link_libraries (music_previews previews-lib unity-shared) \ No newline at end of file +target_link_libraries (music_previews previews-lib unity-shared) + +# +# Music Standalone variant +# +add_executable (movie_previews StandaloneMoviePreview.cpp) +add_dependencies (movie_previews previews-lib) +target_link_libraries (movie_previews previews-lib unity-shared) + +# +# Thumbnail Standalone +# +add_executable (thumbnailer StandaloneThumbnailer.cpp) +add_dependencies (thumbnailer unity-shared) +target_link_libraries (thumbnailer unity-shared) \ No newline at end of file diff --git a/dash/previews/DBusTestRunner.h b/dash/previews/DBusTestRunner.h new file mode 100644 index 000000000..24ffe3713 --- /dev/null +++ b/dash/previews/DBusTestRunner.h @@ -0,0 +1,83 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU 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 GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#ifndef DBUSTESTRUNNER_H +#define DBUSTESTRUNNER_H + + #include <UnityCore/GLibDBusProxy.h> + +#include <UnityCore/Preview.h> +#include <UnityCore/Lens.h> +#include <UnityCore/Results.h> +#include <NuxCore/Logger.h> + +namespace unity +{ +namespace dash +{ +namespace previews +{ +namespace +{ +nux::logging::Logger logger("unity.dash.DBusTestRunner"); +} + + +class DBusTestRunner +{ +public: + typedef std::map<std::string, unity::glib::Variant> Hints; + + DBusTestRunner(std::string const& dbus_name, std::string const& dbus_path, std::string const& interface_name) + : proxy_(nullptr) + , connected_(false) + , dbus_name_(dbus_name) + , dbus_path_(dbus_path) + { + proxy_ = new glib::DBusProxy(dbus_name, dbus_path, interface_name); + proxy_->connected.connect(sigc::mem_fun(this, &DBusTestRunner::OnProxyConnectionChanged)); + proxy_->disconnected.connect(sigc::mem_fun(this, &DBusTestRunner::OnProxyDisconnected)); + } + + virtual void OnProxyConnectionChanged() + { + LOG_DEBUG(logger) << "Dbus connection changed. connected=" << (proxy_->IsConnected() ? "true" : "false"); + } + + virtual void OnProxyDisconnected() + { + LOG_DEBUG(logger) << "Dbus disconnected"; + } + + sigc::signal<void, bool> connected; + + glib::DBusProxy* proxy_; + + bool connected_; + std::string dbus_name_; + std::string dbus_path_; +}; + + +} // namespace previews +} // namespace dash +} // namespace unity + +#endif // DBUSTESTRUNNER_H \ No newline at end of file diff --git a/dash/previews/GenericPreview.cpp b/dash/previews/GenericPreview.cpp index 9152a1de2..ef3898ecd 100644 --- a/dash/previews/GenericPreview.cpp +++ b/dash/previews/GenericPreview.cpp @@ -63,6 +63,7 @@ NUX_IMPLEMENT_OBJECT_TYPE(GenericPreview); GenericPreview::GenericPreview(dash::Preview::Ptr preview_model) : Preview(preview_model) +, full_data_layout_(nullptr) { SetupBackground(); SetupViews(); @@ -143,9 +144,21 @@ void GenericPreview::SetupViews() nux::HLayout* image_data_layout = new nux::HLayout(); image_data_layout->SetSpaceBetweenChildren(style.GetPanelSplitWidth()); + ///////////////////// + // Image + std::string image_hint; + if (preview_model_->image.Get()) + { + glib::String tmp_icon(g_icon_to_string(preview_model_->image.Get())); + image_hint = tmp_icon.Str(); + } image_ = new CoverArt(); - image_->SetImage(preview_model_->image.Get().RawPtr() ? g_icon_to_string(preview_model_->image.Get().RawPtr()) : ""); + if (image_hint.empty()) + image_->GenerateImage(preview_model_->image_source_uri); + else + image_->SetImage(image_hint); image_->SetFont(style.no_preview_image_font()); + ///////////////////// ///////////////////// // Data Panel @@ -162,13 +175,15 @@ void GenericPreview::SetupViews() title_ = new nux::StaticCairoText(preview_model_->title); title_->SetLines(-1); title_->SetFont(style.title_font().c_str()); + preview_data_layout->AddView(title_.GetPointer(), 1); - subtitle_ = new nux::StaticCairoText(preview_model_->subtitle); - subtitle_->SetLines(-1); - subtitle_->SetFont(style.subtitle_size_font().c_str()); - - preview_data_layout->AddView(title_, 1); - preview_data_layout->AddView(subtitle_, 1); + if (!preview_model_->subtitle.Get().empty()) + { + subtitle_ = new nux::StaticCairoText(preview_model_->subtitle); + subtitle_->SetLines(-1); + subtitle_->SetFont(style.subtitle_size_font().c_str()); + preview_data_layout->AddView(subtitle_.GetPointer(), 1); + } ///////////////////// ///////////////////// @@ -180,17 +195,20 @@ void GenericPreview::SetupViews() preview_info_layout->SetSpaceBetweenChildren(12); preview_info->SetLayout(preview_info_layout); - description_ = new nux::StaticCairoText(""); - description_->SetFont(style.description_font().c_str()); - description_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_TOP); - description_->SetLines(-20); - description_->SetLineSpacing(1.5); - description_->SetText(preview_model_->description); + if (!preview_model_->description.Get().empty()) + { + description_ = new nux::StaticCairoText(""); + description_->SetFont(style.description_font().c_str()); + description_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_TOP); + description_->SetLines(-style.GetDescriptionLineCount()); + description_->SetLineSpacing(style.GetDescriptionLineSpacing()); + description_->SetText(preview_model_->description); + preview_info_layout->AddView(description_.GetPointer()); + } - preview_info_layout->AddView(description_); - if (preview_model_->GetInfoHints().size() > 0) + if (!preview_model_->GetInfoHints().empty()) { - PreviewInfoHintWidget* preview_info_hints = new PreviewInfoHintWidget(preview_model_, 24); + PreviewInfoHintWidget* preview_info_hints = new PreviewInfoHintWidget(preview_model_, style.GetInfoHintIconSizeWidth()); preview_info_layout->AddView(preview_info_hints); } ///////////////////// @@ -207,7 +225,8 @@ void GenericPreview::SetupViews() full_data_layout_->AddView(actions_layout, 0); ///////////////////// - image_data_layout->AddView(image_, 0); + image_data_layout->AddView(image_.GetPointer(), 0); + image_data_layout->AddLayout(full_data_layout_, 1); SetLayout(image_data_layout); @@ -231,13 +250,13 @@ long GenericPreview::ComputeContentSize() int details_width = MAX(0, geo.width - geo_art.width - style.GetPanelSplitWidth() - style.GetDetailsLeftMargin() - style.GetDetailsRightMargin()); - title_->SetMaximumWidth(details_width); - subtitle_->SetMaximumWidth(details_width); - description_->SetMaximumWidth(details_width); + if (title_) { title_->SetMaximumWidth(details_width); } + if (subtitle_) { subtitle_->SetMaximumWidth(details_width); } + if (description_) { description_->SetMaximumWidth(details_width); } for (nux::AbstractButton* button : action_buttons_) { - button->SetMinMaxSize(MIN((details_width - style.GetSpaceBetweenActions()) / 2, style.GetActionButtonMaximumWidth()), style.GetActionButtonHeight()); + button->SetMinMaxSize(CLAMP((details_width - style.GetSpaceBetweenActions()) / 2, 0, style.GetActionButtonMaximumWidth()), style.GetActionButtonHeight()); } image_->ComputeContentSize(); diff --git a/dash/previews/GenericPreview.h b/dash/previews/GenericPreview.h index 67f12df89..73fe52141 100644 --- a/dash/previews/GenericPreview.h +++ b/dash/previews/GenericPreview.h @@ -66,10 +66,10 @@ protected: protected: nux::VLayout* full_data_layout_; - CoverArt* image_; - nux::StaticCairoText* title_; - nux::StaticCairoText* subtitle_; - nux::StaticCairoText* description_; + nux::ObjectPtr<CoverArt> image_; + nux::ObjectPtr<nux::StaticCairoText> title_; + nux::ObjectPtr<nux::StaticCairoText> subtitle_; + nux::ObjectPtr<nux::StaticCairoText> description_; typedef std::unique_ptr<nux::AbstractPaintLayer> LayerPtr; LayerPtr details_bg_layer_; diff --git a/dash/previews/StandaloneDBusTestRunner.h b/dash/previews/LensDBusTestRunner.h index 6c8aa7318..0e7fce9bc 100644 --- a/dash/previews/StandaloneDBusTestRunner.h +++ b/dash/previews/LensDBusTestRunner.h @@ -18,12 +18,11 @@ * */ - #include <UnityCore/GLibDBusProxy.h> +#ifndef LENSDBUSTESTRUNNER_H +#define LENSDBUSTESTRUNNER_H -#include <UnityCore/Preview.h> -#include <UnityCore/Lens.h> -#include <UnityCore/Results.h> -#include <NuxCore/Logger.h> + +#include "DBusTestRunner.h" namespace unity { @@ -31,36 +30,24 @@ namespace dash { namespace previews { -namespace -{ -nux::logging::Logger logger("unity.dash.StandaloneDBusTestRunner"); -} - -class StandaloneDBusTestRunner +class LensDBusTestRunner : public DBusTestRunner { public: typedef std::map<std::string, unity::glib::Variant> Hints; - StandaloneDBusTestRunner(std::string const& dbus_name, std::string const& dbus_path) - : proxy_(nullptr) + LensDBusTestRunner(std::string const& dbus_name, std::string const& dbus_path, std::string const& interface_name) + : DBusTestRunner(dbus_name, dbus_path, interface_name) , results_(new Results(ModelType::REMOTE)) , results_variant_(NULL) - , connected_(false) - , dbus_name_(dbus_name) - , dbus_path_(dbus_path) { - proxy_ = new glib::DBusProxy(dbus_name, dbus_path, "com.canonical.Unity.Lens"); - proxy_->connected.connect(sigc::mem_fun(this, &StandaloneDBusTestRunner::OnProxyConnectionChanged)); - proxy_->disconnected.connect(sigc::mem_fun(this, &StandaloneDBusTestRunner::OnProxyDisconnected)); - proxy_->Connect("Changed", sigc::mem_fun(this, &StandaloneDBusTestRunner::OnChanged)); - - results_->end_transaction.connect(sigc::mem_fun(this, &StandaloneDBusTestRunner::ResultsModelUpdated)); + proxy_->Connect("Changed", sigc::mem_fun(this, &LensDBusTestRunner::OnChanged)); + results_->end_transaction.connect(sigc::mem_fun(this, &LensDBusTestRunner::ResultsModelUpdated)); } void OnProxyConnectionChanged() { - LOG_DEBUG(logger) << "Dbus connection changed. connected=" << (proxy_->IsConnected() ? "true" : "false"); + DBusTestRunner::OnProxyConnectionChanged(); if (proxy_->IsConnected()) { @@ -69,14 +56,6 @@ public: } } - void OnProxyDisconnected() - { - LOG_DEBUG(logger) << "Dbus disconnected"; - - connected_ = false; - connected.emit(connected_); - } - void OnChanged(GVariant* parameters) { glib::String dbus_path; @@ -147,7 +126,7 @@ public: g_variant_new("(sa{sv})", search_string.c_str(), &b), - sigc::mem_fun(this, &StandaloneDBusTestRunner::OnSearchFinished), + sigc::mem_fun(this, &LensDBusTestRunner::OnSearchFinished), search_cancellable_); g_variant_builder_clear(&b); @@ -190,8 +169,6 @@ public: } } - - void Preview(std::string const& uri) { LOG_DEBUG(logger) << "Previewing '" << uri << "' on '" << dbus_name_ << "'"; @@ -211,7 +188,7 @@ public: proxy_->Call("Activate", g_variant_new("(su)", uri.c_str(), UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_RESULT), - sigc::mem_fun(this, &StandaloneDBusTestRunner::ActivationReply), + sigc::mem_fun(this, &LensDBusTestRunner::ActivationReply), preview_cancellable_); } @@ -250,7 +227,6 @@ public: } - sigc::signal<void, bool> connected; sigc::signal<void, std::string const&, dash::Preview::Ptr> preview_ready; sigc::signal<void, Hints const&> search_finished; @@ -276,22 +252,15 @@ protected: } - glib::DBusProxy* proxy_; glib::Object<GCancellable> preview_cancellable_; glib::Object<GCancellable> search_cancellable_; Results::Ptr results_; GVariant *results_variant_; - - bool connected_; - std::string dbus_name_; - std::string dbus_path_; }; - - - - } // namespace previews } // namespace dash -} // namespace unity \ No newline at end of file +} // namespace unity + +#endif // LENSDBUSTESTRUNNER_H \ No newline at end of file diff --git a/dash/previews/MoviePreview.cpp b/dash/previews/MoviePreview.cpp index 45a8f4a96..7eafd2e29 100644 --- a/dash/previews/MoviePreview.cpp +++ b/dash/previews/MoviePreview.cpp @@ -34,6 +34,7 @@ #include "MoviePreview.h" #include "PreviewInfoHintWidget.h" +#include "PreviewRatingsWidget.h" namespace unity { @@ -63,6 +64,7 @@ NUX_IMPLEMENT_OBJECT_TYPE(MoviePreview); MoviePreview::MoviePreview(dash::Preview::Ptr preview_model) : Preview(preview_model) +, full_data_layout_(nullptr) { SetupBackground(); SetupView(); @@ -148,16 +150,30 @@ void MoviePreview::SetupView() { dash::MoviePreview* movie_preview_model = dynamic_cast<dash::MoviePreview*>(preview_model_.get()); if (!movie_preview_model) + { + LOG_ERROR(logger) << "Could not derive movie preview model from given parameter."; return; - + } previews::Style& style = dash::previews::Style::Instance(); nux::HLayout* image_data_layout = new nux::HLayout(); image_data_layout->SetSpaceBetweenChildren(style.GetPanelSplitWidth()); - CoverArt* image = new CoverArt(); - image->SetImage(preview_model_->image.Get().RawPtr() ? g_icon_to_string(preview_model_->image.Get().RawPtr()) : ""); - image->SetFont(style.no_preview_image_font()); + ///////////////////// + // Image + std::string image_hint; + if (preview_model_->image.Get()) + { + glib::String tmp_icon(g_icon_to_string(preview_model_->image.Get())); + image_hint = tmp_icon.Str(); + } + image_ = new CoverArt(); + if (image_hint.empty()) + image_->GenerateImage(preview_model_->image_source_uri); + else + image_->SetImage(image_hint); + image_->SetFont(style.no_preview_image_font()); + ///////////////////// ///////////////////// // Data Panel @@ -174,14 +190,22 @@ void MoviePreview::SetupView() title_ = new nux::StaticCairoText(preview_model_->title); title_->SetLines(-1); title_->SetFont(style.title_font().c_str()); + app_data_layout->AddView(title_.GetPointer(), 1); - subtitle_ = new nux::StaticCairoText(preview_model_->subtitle); - subtitle_->SetLines(-1); - subtitle_->SetFont(style.subtitle_size_font().c_str()); - - app_data_layout->AddView(title_, 1); - app_data_layout->AddView(subtitle_, 1); + if (!preview_model_->subtitle.Get().empty()) + { + subtitle_ = new nux::StaticCairoText(preview_model_->subtitle); + subtitle_->SetLines(-1); + subtitle_->SetFont(style.subtitle_size_font().c_str()); + app_data_layout->AddView(subtitle_.GetPointer(), 1); + } ///////////////////// + + rating_ = new PreviewRatingsWidget(); + rating_->SetMaximumHeight(style.GetRatingWidgetHeight()); + rating_->SetMinimumHeight(style.GetRatingWidgetHeight()); + rating_->SetRating(movie_preview_model->rating); + rating_->SetReviews(movie_preview_model->num_ratings); ///////////////////// // Description @@ -192,18 +216,21 @@ void MoviePreview::SetupView() preview_info_layout->SetSpaceBetweenChildren(12); preview_info->SetLayout(preview_info_layout); - description_ = new nux::StaticCairoText(""); - description_->SetFont(style.description_font().c_str()); - description_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_TOP); - description_->SetLines(-20); - description_->SetLineSpacing(1.5); - description_->SetText(preview_model_->description); + if (!preview_model_->GetInfoHints().empty()) + { + PreviewInfoHintWidget* preview_info_hints = new PreviewInfoHintWidget(preview_model_, style.GetInfoHintIconSizeWidth(), false, true); + preview_info_layout->AddView(preview_info_hints, 0); + } - preview_info_layout->AddView(description_); - if (preview_model_->GetInfoHints().size() > 0) + if (!preview_model_->description.Get().empty()) { - PreviewInfoHintWidget* preview_info_hints = new PreviewInfoHintWidget(preview_model_, 24); - preview_info_layout->AddView(preview_info_hints); + description_ = new nux::StaticCairoText(""); + description_->SetFont(style.description_font().c_str()); + description_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_TOP); + description_->SetLines(-style.GetDescriptionLineCount()); + description_->SetLineSpacing(style.GetDescriptionLineSpacing()); + description_->SetText(preview_model_->description); + preview_info_layout->AddView(description_.GetPointer()); } ///////////////////// @@ -215,11 +242,12 @@ void MoviePreview::SetupView() /////////////////// full_data_layout_->AddLayout(app_data_layout, 0); + full_data_layout_->AddView(rating_.GetPointer(), 0); full_data_layout_->AddView(preview_info, 1); full_data_layout_->AddView(actions_layout, 0); ///////////////////// - image_data_layout->AddView(image, 0); + image_data_layout->AddView(image_.GetPointer(), 0); image_data_layout->AddLayout(full_data_layout_, 1); SetLayout(image_data_layout); @@ -233,7 +261,7 @@ long MoviePreview::ComputeContentSize() previews::Style& style = dash::previews::Style::Instance(); - nux::Geometry geo_art(geo.x, geo.y, style.GetAppImageAspectRatio() * geo.height, geo.height); + nux::Geometry geo_art(geo.x, geo.y, style.GetVideoImageAspectRatio() * geo.height, geo.height); if (geo.width - geo_art.width - style.GetPanelSplitWidth() - style.GetDetailsLeftMargin() - style.GetDetailsRightMargin() < style.GetDetailsPanelMinimumWidth()) geo_art.width = MAX(0, geo.width - style.GetPanelSplitWidth() - style.GetDetailsLeftMargin() - style.GetDetailsRightMargin() - style.GetDetailsPanelMinimumWidth()); @@ -244,13 +272,13 @@ long MoviePreview::ComputeContentSize() int details_width = MAX(0, geo.width - geo_art.width - style.GetPanelSplitWidth() - style.GetDetailsLeftMargin() - style.GetDetailsRightMargin()); - title_->SetMaximumWidth(details_width); - subtitle_->SetMaximumWidth(details_width); - description_->SetMaximumWidth(details_width); + if (title_) { title_->SetMaximumWidth(details_width); } + if (subtitle_) { subtitle_->SetMaximumWidth(details_width); } + if (description_) { description_->SetMaximumWidth(details_width); } for (nux::AbstractButton* button : action_buttons_) { - button->SetMinMaxSize(MIN((details_width - style.GetSpaceBetweenActions()) / 2, style.GetActionButtonMaximumWidth()), style.GetActionButtonHeight()); + button->SetMinMaxSize(CLAMP((details_width - style.GetSpaceBetweenActions()) / 2, 0, style.GetActionButtonMaximumWidth()), style.GetActionButtonHeight()); } image_->ComputeContentSize(); diff --git a/dash/previews/MoviePreview.h b/dash/previews/MoviePreview.h index 2fd6a1797..bc4b39872 100644 --- a/dash/previews/MoviePreview.h +++ b/dash/previews/MoviePreview.h @@ -40,6 +40,7 @@ namespace dash namespace previews { class CoverArt; +class PreviewRatingsWidget; class MoviePreview : public Preview { @@ -68,10 +69,11 @@ protected: protected: nux::VLayout* full_data_layout_; - CoverArt* image_; - nux::StaticCairoText* title_; - nux::StaticCairoText* subtitle_; - nux::StaticCairoText* description_; + nux::ObjectPtr<CoverArt> image_; + nux::ObjectPtr<PreviewRatingsWidget> rating_; + nux::ObjectPtr<nux::StaticCairoText> title_; + nux::ObjectPtr<nux::StaticCairoText> subtitle_; + nux::ObjectPtr<nux::StaticCairoText> description_; typedef std::unique_ptr<nux::AbstractPaintLayer> LayerPtr; LayerPtr details_bg_layer_; diff --git a/dash/previews/MusicPreview.cpp b/dash/previews/MusicPreview.cpp index e414bd584..d865240ee 100644 --- a/dash/previews/MusicPreview.cpp +++ b/dash/previews/MusicPreview.cpp @@ -53,6 +53,9 @@ NUX_IMPLEMENT_OBJECT_TYPE(MusicPreview); MusicPreview::MusicPreview(dash::Preview::Ptr preview_model) : Preview(preview_model) +, image_(nullptr) +, title_(nullptr) +, subtitle_(nullptr) { SetupBackground(); SetupViews(); @@ -130,16 +133,30 @@ void MusicPreview::SetupViews() { dash::MusicPreview* music_preview_model = dynamic_cast<dash::MusicPreview*>(preview_model_.get()); if (!music_preview_model) + { + LOG_ERROR(logger) << "Could not derive music preview model from given parameter."; return; - + } previews::Style& style = dash::previews::Style::Instance(); nux::HLayout* image_data_layout = new nux::HLayout(); image_data_layout->SetSpaceBetweenChildren(style.GetPanelSplitWidth()); + ///////////////////// + // Image + std::string image_hint; + if (preview_model_->image.Get()) + { + glib::String tmp_icon(g_icon_to_string(preview_model_->image.Get())); + image_hint = tmp_icon.Str(); + } image_ = new CoverArt(); - image_->SetImage(preview_model_->image.Get().RawPtr() ? g_icon_to_string(preview_model_->image.Get().RawPtr()) : ""); + if (image_hint.empty()) + image_->GenerateImage(preview_model_->image_source_uri); + else + image_->SetImage(image_hint); image_->SetFont(style.no_preview_image_font()); + ///////////////////// ///////////////////// // App Data Panel @@ -155,17 +172,18 @@ void MusicPreview::SetupViews() title_ = new nux::StaticCairoText(preview_model_->title); title_->SetFont(style.title_font().c_str()); title_->SetLines(-1); + album_data_layout->AddView(title_.GetPointer(), 1); - subtitle_ = new nux::StaticCairoText(preview_model_->subtitle); - subtitle_->SetFont(style.subtitle_size_font().c_str()); - subtitle_->SetLines(-1); - - album_data_layout->AddView(title_, 1); - album_data_layout->AddView(subtitle_, 1); + if (!preview_model_->subtitle.Get().empty()) + { + subtitle_ = new nux::StaticCairoText(preview_model_->subtitle); + subtitle_->SetFont(style.subtitle_size_font().c_str()); + subtitle_->SetLines(-1); + album_data_layout->AddView(subtitle_.GetPointer(), 1); + } ///////////////////// - ///////////////////// // Music Tracks dash::Tracks::Ptr tracks_model = music_preview_model->GetTracksModel(); @@ -184,12 +202,12 @@ void MusicPreview::SetupViews() // Hints && Actions nux::VLayout* hints_layout = NULL; nux::Layout* actions_layout = NULL; - if (preview_model_->GetInfoHints().size() > 0) + if (!preview_model_->GetInfoHints().empty()) { hints_layout = new nux::VLayout(); hints_layout->SetSpaceBetweenChildren(0); hints_layout->AddSpace(0, 1); - PreviewInfoHintWidget* preview_info_hints = new PreviewInfoHintWidget(preview_model_, 24); + PreviewInfoHintWidget* preview_info_hints = new PreviewInfoHintWidget(preview_model_, style.GetInfoHintIconSizeWidth()); hints_layout->AddView(preview_info_hints, 0); // If there are actions, we use a vertical layout @@ -199,7 +217,7 @@ void MusicPreview::SetupViews() } else // otherwise we add a grid layout. { - hint_actions_layout->AddSpace(0,1); + hint_actions_layout->AddSpace(0, 1); action_buttons_.clear(); actions_layout = BuildGridActionsLayout(preview_model_->GetActions(), action_buttons_); actions_layout->SetLeftAndRightPadding(0, style.GetDetailsRightMargin()); @@ -212,12 +230,12 @@ void MusicPreview::SetupViews() full_data_layout_->AddLayout(album_data_layout, 0); if (tracks_) { - full_data_layout_->AddView(tracks_, 1); + full_data_layout_->AddView(tracks_.GetPointer(), 1); } full_data_layout_->AddLayout(hint_actions_layout, 0); ///////////////////// - image_data_layout->AddView(image_, 0); + image_data_layout->AddView(image_.GetPointer(), 0); image_data_layout->AddLayout(full_data_layout_, 1); SetLayout(image_data_layout); @@ -265,12 +283,12 @@ long MusicPreview::ComputeContentSize() int details_width = MAX(0, geo.width - geo_art.width - style.GetPanelSplitWidth() - style.GetDetailsLeftMargin() - style.GetDetailsRightMargin()); - title_->SetMaximumWidth(details_width); - subtitle_->SetMaximumWidth(details_width); + if (title_) { title_->SetMaximumWidth(details_width); } + if (subtitle_) { subtitle_->SetMaximumWidth(details_width); } for (nux::AbstractButton* button : action_buttons_) { - button->SetMinMaxSize(MIN((details_width - style.GetSpaceBetweenActions()) / 2, style.GetActionButtonMaximumWidth()), style.GetActionButtonHeight()); + button->SetMinMaxSize(CLAMP((details_width - style.GetSpaceBetweenActions()) / 2, 0, style.GetActionButtonMaximumWidth()), style.GetActionButtonHeight()); } image_->ComputeContentSize(); diff --git a/dash/previews/MusicPreview.h b/dash/previews/MusicPreview.h index 5f1fb3e14..ddfac4bac 100644 --- a/dash/previews/MusicPreview.h +++ b/dash/previews/MusicPreview.h @@ -68,11 +68,12 @@ protected: void OnPauseTrack(std::string const& uri); private: - CoverArt* image_; - nux::StaticCairoText* title_; - nux::StaticCairoText* subtitle_; nux::VLayout* full_data_layout_; - nux::View* tracks_; + + nux::ObjectPtr<CoverArt> image_; + nux::ObjectPtr<nux::StaticCairoText> title_; + nux::ObjectPtr<nux::StaticCairoText> subtitle_; + nux::ObjectPtr<nux::View> tracks_; typedef std::unique_ptr<nux::AbstractPaintLayer> LayerPtr; LayerPtr details_bg_layer_; diff --git a/dash/previews/Preview.cpp b/dash/previews/Preview.cpp index 423ff6c8c..ae9440a80 100644 --- a/dash/previews/Preview.cpp +++ b/dash/previews/Preview.cpp @@ -53,30 +53,30 @@ previews::Preview::Ptr Preview::PreviewForModel(dash::Preview::Ptr model) return previews::Preview::Ptr(); } - // if (model->renderer_name == "preview-generic") - // { - // return Preview::Ptr(new GenericPreview(model)); - // } - // else if (model->renderer_name == "preview-application") - // { - // return Preview::Ptr(new ApplicationPreview(model)); - // } - // else if (model->renderer_name == "preview-music") - // { - // return Preview::Ptr(new MusicPreview(model)); - // } - // else if (model->renderer_name == "preview-movie") - // { - // return Preview::Ptr(new MoviePreview(model)); - // } - // // else if (renderer_name == "preview-series") - // // { - // // return Preview::Ptr(new SeriesPreview(model)); - // // } - // else + if (model->renderer_name == "preview-generic") + { + return Preview::Ptr(new GenericPreview(model)); + } + else if (model->renderer_name == "preview-application") + { + return Preview::Ptr(new ApplicationPreview(model)); + } + else if (model->renderer_name == "preview-music") + { + return Preview::Ptr(new MusicPreview(model)); + } + else if (model->renderer_name == "preview-movie") + { + return Preview::Ptr(new MoviePreview(model)); + } + // else if (renderer_name == "preview-series") // { - // LOG_WARN(logger) << "Unable to create Preview for renderer: " << model->renderer_name.Get() << "; using generic"; + // return Preview::Ptr(new SeriesPreview(model)); // } + else + { + LOG_WARN(logger) << "Unable to create Preview for renderer: " << model->renderer_name.Get() << "; using generic"; + } return Preview::Ptr(new GenericPreview(model)); } @@ -102,7 +102,7 @@ void Preview::AddProperties(GVariantBuilder* builder) { } -void Preview::OnActionActivated(nux::AbstractButton* button, std::string const& id) +void Preview::OnActionActivated(ActionButton* button, std::string const& id) { if (preview_model_) preview_model_->PerformAction(id); diff --git a/dash/previews/Preview.h b/dash/previews/Preview.h index a8170d484..d4ae1e74a 100644 --- a/dash/previews/Preview.h +++ b/dash/previews/Preview.h @@ -39,6 +39,8 @@ namespace unity { namespace dash { +class ActionButton; + namespace previews { @@ -61,7 +63,7 @@ protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw) {} virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) {} - virtual void OnActionActivated(nux::AbstractButton* button, std::string const& id); + virtual void OnActionActivated(ActionButton* button, std::string const& id); virtual void OnNavigateIn() {} virtual void OnNavigateInComplete() {} diff --git a/dash/previews/PreviewContainer.cpp b/dash/previews/PreviewContainer.cpp index 95229ad5c..57cb4d5a5 100644 --- a/dash/previews/PreviewContainer.cpp +++ b/dash/previews/PreviewContainer.cpp @@ -303,6 +303,23 @@ void PreviewContainer::SetupViews() }); } +void PreviewContainer::ProcessDraw(nux::GraphicsEngine& gfx_engine, bool force_draw) +{ + bool bNextQueue = AnimationInProgress(); + // rely on the compiz event loop to come back to us in a nice throttling + if (!bNextQueue && content_layout_ && content_layout_->IsAnimating()) + { + content_layout_->UpdateAnimationProgress(1.0f, 1.0f); + } + + View::ProcessDraw(gfx_engine, force_draw); + + if (bNextQueue) + { + QueueAnimation(); + } +} + void PreviewContainer::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { } @@ -315,16 +332,6 @@ void PreviewContainer::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_d // Paint using ProcessDraw2. ProcessDraw is overrided by empty impl so we can control z order. if (content_layout_) { - // rely on the compiz event loop to come back to us in a nice throttling - if (AnimationInProgress()) - { - QueueAnimation(); - } - else if (content_layout_ && content_layout_->IsAnimating()) - { - content_layout_->UpdateAnimationProgress(1.0f, 1.0f); - } - content_layout_->ProcessDraw2(gfx_engine, force_draw); } if (GetCompositionLayout()) @@ -351,22 +358,24 @@ bool PreviewContainer::AnimationInProgress() static float easeInOutQuart(float t) { + t = CLAMP(t, 0.0, 1.0); t*=2.0f; - if (t < 1) return 0.5f*t*t*t*t; + if (t < 1.0) return 0.5f*pow(t, 4); else { t -= 2.0f; - return -0.5f * (t*t*t*t- 2); + return -0.5f * (pow(t, 4)- 2); } } // static float easeInOutCubic(float t) // { +// t = CLAMP(t, 0.0, 1.0); // t*=2.0f; // if(t < 1.0f) { -// return 0.5f*t*t*t; +// return 0.5f*pow(t, 3); // } else { // t -= 2.0f; -// return 0.5f*(t*t*t + 2); +// return 0.5f*(pow(t, 3) + 2); // } // } @@ -380,15 +389,15 @@ float PreviewContainer::GetSwipeAnimationProgress(struct timespec const& current void PreviewContainer::QueueAnimation() { - if (!idle_) - { - idle_.reset(new glib::Idle(sigc::mem_fun(this, &PreviewContainer::Iteration), glib::Source::Priority::DEFAULT)); - } -} +// if (!idle_) +// { +// idle_.reset(new glib::Idle(sigc::mem_fun(this, &PreviewContainer::Iteration), glib::Source::Priority::DEFAULT)); +// } +// } -bool PreviewContainer::Iteration() -{ - idle_.reset(); +// bool PreviewContainer::Iteration() +// { + // idle_.reset(); timespec current; clock_gettime(CLOCK_MONOTONIC, ¤t); @@ -397,7 +406,6 @@ bool PreviewContainer::Iteration() last_progress_time_ = current; QueueDraw(); - return false; } bool PreviewContainer::AcceptKeyNavFocus() diff --git a/dash/previews/PreviewContainer.h b/dash/previews/PreviewContainer.h index af0bd98ea..e0d502b55 100644 --- a/dash/previews/PreviewContainer.h +++ b/dash/previews/PreviewContainer.h @@ -75,6 +75,8 @@ public: sigc::signal<void> request_close; bool AcceptKeyNavFocus(); + + void ProcessDraw(nux::GraphicsEngine& gfx_engine, bool force_draw); protected: void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw); @@ -92,7 +94,6 @@ private: float GetSwipeAnimationProgress(struct timespec const& current) const; void QueueAnimation(); - bool Iteration(); private: // View related diff --git a/dash/previews/PreviewInfoHintWidget.cpp b/dash/previews/PreviewInfoHintWidget.cpp index 79618aff8..6368ec8fd 100644 --- a/dash/previews/PreviewInfoHintWidget.cpp +++ b/dash/previews/PreviewInfoHintWidget.cpp @@ -35,14 +35,21 @@ namespace dash { namespace previews { +namespace +{ nux::logging::Logger logger("unity.dash.previews.previewinfohintwidget"); +const int layout_spacing = 12; +} + NUX_IMPLEMENT_OBJECT_TYPE(PreviewInfoHintWidget); -PreviewInfoHintWidget::PreviewInfoHintWidget(dash::Preview::Ptr preview_model, int icon_size) - : View(NUX_TRACKER_LOCATION) - , icon_size_(icon_size) - , preview_model_(preview_model) +PreviewInfoHintWidget::PreviewInfoHintWidget(dash::Preview::Ptr preview_model, int icon_size, bool visible_icons, bool condensed_format) +: View(NUX_TRACKER_LOCATION) +, icon_size_(icon_size) +, condensed_format_(condensed_format) +, visible_icons_(visible_icons) +, preview_model_(preview_model) { SetupViews(); } @@ -51,6 +58,20 @@ PreviewInfoHintWidget::~PreviewInfoHintWidget() { } +void PreviewInfoHintWidget::SetVisibleIcons(bool visible) +{ + visible_icons_ = visible; + SetupViews(); + QueueDraw(); +} + +void PreviewInfoHintWidget::SetCondensedFormat(bool condensed) +{ + condensed_format_ = condensed; + SetupViews(); + QueueDraw(); +} + void PreviewInfoHintWidget::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) { } @@ -134,6 +155,9 @@ std::string StringFromVariant(GVariant* variant) void PreviewInfoHintWidget::SetupViews() { + RemoveLayout(); + info_hints_.clear(); + previews::Style& style = previews::Style::Instance(); nux::VLayout* layout = new nux::VLayout(); @@ -142,32 +166,40 @@ void PreviewInfoHintWidget::SetupViews() for (dash::Preview::InfoHintPtr info_hint : preview_model_->GetInfoHints()) { nux::HLayout* hint_layout = new nux::HLayout(); - hint_layout->SetSpaceBetweenChildren(16); + hint_layout->SetSpaceBetweenChildren(layout_spacing); - IconTexture* info_icon = new IconTexture(info_hint->icon_hint, icon_size_); - info_icon->SetMinimumSize(icon_size_, icon_size_); - info_icon->SetVisible(true); - hint_layout->AddView(info_icon, 0); + if (visible_icons_) + { + IconTexture* info_icon = new IconTexture(info_hint->icon_hint, icon_size_); + info_icon->SetMinimumSize(icon_size_, icon_size_); + info_icon->SetVisible(true); + hint_layout->AddView(info_icon, 0); + } - if (info_hint->display_name.size() > 0) + StaticCairoTextPtr info_name; + if (!info_hint->display_name.empty()) { - nux::StaticCairoText* info_name = new nux::StaticCairoText(info_hint->display_name, NUX_TRACKER_LOCATION); - info_name->SetFont(style.info_hint_font()); - - nux::Layout* info_name_layout = new nux::HLayout(); - info_name_layout->AddView(info_name, 1, nux::MINOR_POSITION_CENTER); - info_name_layout->SetMaximumWidth(128 - (icon_size_ > 0 ? (icon_size_ + 16) : 0)); - - hint_layout->AddView(info_name_layout, 1); + std::string tmp_display_name = info_hint->display_name; + if (condensed_format_) { tmp_display_name += ":"; } + + info_name = new nux::StaticCairoText(tmp_display_name, NUX_TRACKER_LOCATION); + info_name->SetFont(condensed_format_ ? style.info_hint_bold_font() : style.info_hint_font()); + info_name->SetLines(-1); + if (!condensed_format_) + { + info_name->SetMinimumWidth(style.GetInfoHintNameWidth()); + info_name->SetMaximumWidth(style.GetInfoHintNameWidth()); + } + hint_layout->AddView(info_name.GetPointer(), 0, nux::MINOR_POSITION_CENTER); } - - nux::StaticCairoText* info_value = new nux::StaticCairoText(StringFromVariant(info_hint->value), NUX_TRACKER_LOCATION); + StaticCairoTextPtr info_value(new nux::StaticCairoText(StringFromVariant(info_hint->value), NUX_TRACKER_LOCATION)); info_value->SetFont(style.info_hint_font()); - nux::Layout* info_value_layout = new nux::HLayout(); - info_value_layout->AddView(info_value, 1, nux::MINOR_POSITION_CENTER); + info_value->SetLines(-1); + hint_layout->AddView(info_value.GetPointer(), 1, nux::MINOR_POSITION_CENTER); - hint_layout->AddView(info_value_layout, 1); + InfoHint info_hint_views(info_name, info_value); + info_hints_.push_back(info_hint_views); layout->AddLayout(hint_layout, 0); } @@ -175,6 +207,41 @@ void PreviewInfoHintWidget::SetupViews() SetLayout(layout); } + +long PreviewInfoHintWidget::ComputeContentSize() +{ + previews::Style& style = previews::Style::Instance(); + nux::Geometry const& geo = GetGeometry(); + + for (InfoHint const& info_hint : info_hints_) + { + int max_info_value_width = geo.width; + if (visible_icons_) + { + max_info_value_width -= icon_size_; + max_info_value_width -= layout_spacing; + } + if (info_hint.first) + { + if (condensed_format_) + { + max_info_value_width -= info_hint.first->GetTextExtents().width; + } + else + { + max_info_value_width -= style.GetInfoHintNameWidth(); + } + max_info_value_width -= layout_spacing; + } + + if (info_hint.second) + info_hint.second->SetMaximumWidth(max_info_value_width); + } + + return View::ComputeContentSize(); +} + + } // namespace previews } // namespace dash } // namespace unity diff --git a/dash/previews/PreviewInfoHintWidget.h b/dash/previews/PreviewInfoHintWidget.h index c23af7c43..2a77b4343 100644 --- a/dash/previews/PreviewInfoHintWidget.h +++ b/dash/previews/PreviewInfoHintWidget.h @@ -28,6 +28,10 @@ #include <UnityCore/Preview.h> #include "unity-shared/Introspectable.h" +namespace nux +{ +class StaticCairoText; +} namespace unity { @@ -42,12 +46,17 @@ public: typedef nux::ObjectPtr<PreviewInfoHintWidget> Ptr; NUX_DECLARE_OBJECT_TYPE(PreviewInfoHintWidget, nux::View); - PreviewInfoHintWidget(dash::Preview::Ptr preview_model, int icon_size); + PreviewInfoHintWidget(dash::Preview::Ptr preview_model, int icon_size, bool visible_icons = true, bool condensed_format = false); virtual ~PreviewInfoHintWidget(); + void SetVisibleIcons(bool visible); + void SetCondensedFormat(bool condensed); + // From debug::Introspectable std::string GetName() const; void AddProperties(GVariantBuilder* builder); + + long ComputeContentSize(); protected: virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); @@ -63,6 +72,12 @@ protected: protected: int icon_size_; + bool condensed_format_; + bool visible_icons_; + + typedef nux::ObjectPtr<nux::StaticCairoText> StaticCairoTextPtr; + typedef std::pair<StaticCairoTextPtr, StaticCairoTextPtr> InfoHint; + std::list<InfoHint> info_hints_; dash::Preview::Ptr preview_model_; typedef nux::ObjectPtr<nux::BaseTexture> BaseTexturePtr; diff --git a/dash/previews/PreviewNavigator.cpp b/dash/previews/PreviewNavigator.cpp index 10994131e..9227a9a93 100644 --- a/dash/previews/PreviewNavigator.cpp +++ b/dash/previews/PreviewNavigator.cpp @@ -38,6 +38,7 @@ namespace previews namespace { nux::logging::Logger logger("unity.dash.previews.previewnavigator"); + } NUX_IMPLEMENT_OBJECT_TYPE(PreviewNavigator); @@ -89,6 +90,8 @@ void PreviewNavigator::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_d void PreviewNavigator::SetupViews() { + previews::Style& style = dash::previews::Style::Instance(); + if (orientation_ == Orientation::LEFT || orientation_ == Orientation::RIGHT) { nux::VLayout* vlayout = new nux::VLayout(); @@ -98,9 +101,10 @@ void PreviewNavigator::SetupViews() layout_ = hlayout; if (orientation_ == Orientation::LEFT) - texture_ = new IconTexture(Style::Instance().GetNavLeftIcon(), 24, 24); + texture_ = new IconTexture(Style::Instance().GetNavLeftIcon(), style.GetNavigatorIconSize(), style.GetNavigatorIconSize()); else - texture_ = new IconTexture(Style::Instance().GetNavRightIcon(), 24, 24); + texture_ = new IconTexture(Style::Instance().GetNavRightIcon(), style.GetNavigatorIconSize(), style.GetNavigatorIconSize()); + texture_->SetDrawMode(IconTexture::DrawMode::STRETCH_WITH_ASPECT); vlayout->AddSpace(0,1); vlayout->AddLayout(hlayout); @@ -126,10 +130,6 @@ void PreviewNavigator::SetupViews() if (texture_) { texture_->mouse_click.connect([&](int, int, unsigned long, unsigned long) { activated.emit(); }); - texture_->SetMinimumHeight(32); - texture_->SetMaximumHeight(32); - texture_->SetMinimumWidth(32); - texture_->SetMaximumWidth(32); layout_->AddView(texture_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); } diff --git a/dash/previews/StandaloneApplicationPreview.cpp b/dash/previews/StandaloneApplicationPreview.cpp index 2904c9f84..bae07ecb3 100644 --- a/dash/previews/StandaloneApplicationPreview.cpp +++ b/dash/previews/StandaloneApplicationPreview.cpp @@ -33,6 +33,7 @@ #include "unity-shared/UnitySettings.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" +#include "unity-shared/ThumbnailGenerator.h" #include "Preview.h" #include "PreviewContainer.h" @@ -158,14 +159,13 @@ void TestRunner::Init () The service allows users to communicate with peers by voice, video, and instant messaging over the Internet. Phone calls may be placed to recipients on the traditional telephone networks. Calls to other users within the Skype service are free of charge, while calls to landline telephones and mobile phones are charged via a debit-based user account system."; // creates a generic preview object - glib::Object<GIcon> icon(g_icon_new_for_string("accessories", NULL)); glib::Object<GIcon> iconHint1(g_icon_new_for_string("/usr/share/unity/5/lens-nav-music.svg", NULL)); glib::Object<GIcon> iconHint2(g_icon_new_for_string("/usr/share/unity/5/lens-nav-home.svg", NULL)); glib::Object<GIcon> iconHint3(g_icon_new_for_string("/usr/share/unity/5/lens-nav-people.svg", NULL)); glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); - unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string("~/SkypeIcon.png", NULL)); + unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string("/home/nick/SkypeIcon.png", NULL)); unity_protocol_application_preview_set_license(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "Proprietary"); unity_protocol_application_preview_set_copyright(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "(c) Skype 2012"); unity_protocol_application_preview_set_last_update(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "11th Apr 2012"); @@ -173,11 +173,10 @@ The service allows users to communicate with peers by voice, video, and instant unity_protocol_application_preview_set_num_ratings(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 17); - unity_protocol_preview_set_image(proto_obj, g_icon_new_for_string("~/Skype.png", NULL)); + unity_protocol_preview_set_image_source_uri(proto_obj, "file:///home/nick/Skype.png"); unity_protocol_preview_set_title(proto_obj, app_name.str().c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); - unity_protocol_preview_set_image(proto_obj, icon); unity_protocol_preview_add_action(proto_obj, "uninstall", "Uninstall", iconHint1, 0); unity_protocol_preview_add_action(proto_obj, "launch", "Launch", iconHint2, 0); unity_protocol_preview_add_info_hint(proto_obj, "time", "Total time", iconHint1, g_variant_new("s", "16 h 34miin 45sec")); @@ -194,7 +193,7 @@ The service allows users to communicate with peers by voice, video, and instant void TestRunner::NavRight() { - std::stringstream app_name; + std::stringstream app_name; app_name << "Title " << ++nav_iter; const char* subtitle = "Version 3.2, Size 32 MB"; @@ -202,7 +201,6 @@ void TestRunner::NavRight() The service allows users to communicate with peers by voice, video, and instant messaging over the Internet. Phone calls may be placed to recipients on the traditional telephone networks. Calls to other users within the Skype service are free of charge, while calls to landline telephones and mobile phones are charged via a debit-based user account system."; // creates a generic preview object - glib::Object<GIcon> icon(g_icon_new_for_string("accessories", NULL)); glib::Object<GIcon> iconHint1(g_icon_new_for_string("/usr/share/unity/5/lens-nav-music.svg", NULL)); glib::Object<GIcon> iconHint2(g_icon_new_for_string("/usr/share/unity/5/lens-nav-home.svg", NULL)); glib::Object<GIcon> iconHint3(g_icon_new_for_string("/usr/share/unity/5/lens-nav-people.svg", NULL)); @@ -211,18 +209,17 @@ The service allows users to communicate with peers by voice, video, and instant - unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string("SkypeIcon.png", NULL)); + unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string("/home/nick/SkypeIcon.png", NULL)); unity_protocol_application_preview_set_license(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "Proprietary"); unity_protocol_application_preview_set_copyright(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "(c) Skype 2012"); unity_protocol_application_preview_set_last_update(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "11th Apr 2012"); unity_protocol_application_preview_set_rating(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 0.25); unity_protocol_application_preview_set_num_ratings(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 5); - unity_protocol_preview_set_image(proto_obj, g_icon_new_for_string("Skype.png", NULL)); + unity_protocol_preview_set_image_source_uri(proto_obj, "file:///home/nick/Skype.png"); unity_protocol_preview_set_title(proto_obj, app_name.str().c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); - unity_protocol_preview_set_image(proto_obj, icon); unity_protocol_preview_add_action(proto_obj, "uninstall", "Uninstall", iconHint1, 0); unity_protocol_preview_add_action(proto_obj, "launch", "Launch", iconHint2, 0); unity_protocol_preview_add_info_hint(proto_obj, "time", "Total time", iconHint1, g_variant_new("s", "16 h 34miin 45sec")); @@ -238,7 +235,7 @@ The service allows users to communicate with peers by voice, video, and instant void TestRunner::NavLeft() { - std::stringstream app_name; + std::stringstream app_name; app_name << "Title " << --nav_iter; const char* subtitle = "Version 3.2, Size 32 MB"; @@ -246,7 +243,6 @@ void TestRunner::NavLeft() The service allows users to communicate with peers by voice, video, and instant messaging over the Internet. Phone calls may be placed to recipients on the traditional telephone networks. Calls to other users within the Skype service are free of charge, while calls to landline telephones and mobile phones are charged via a debit-based user account system."; // creates a generic preview object - glib::Object<GIcon> icon(g_icon_new_for_string("accessories", NULL)); glib::Object<GIcon> iconHint1(g_icon_new_for_string("/usr/share/unity/5/lens-nav-music.svg", NULL)); glib::Object<GIcon> iconHint2(g_icon_new_for_string("/usr/share/unity/5/lens-nav-home.svg", NULL)); glib::Object<GIcon> iconHint3(g_icon_new_for_string("/usr/share/unity/5/lens-nav-people.svg", NULL)); @@ -254,18 +250,17 @@ The service allows users to communicate with peers by voice, video, and instant glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); - unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string("SkypeIcon.png", NULL)); + unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string("/home/nick/SkypeIcon.png", NULL)); unity_protocol_application_preview_set_license(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "Proprietary"); unity_protocol_application_preview_set_copyright(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "(c) Skype 2012"); unity_protocol_application_preview_set_last_update(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "11th Apr 2012"); unity_protocol_application_preview_set_rating(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 0.8); unity_protocol_application_preview_set_num_ratings(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 1223); - unity_protocol_preview_set_image(proto_obj, g_icon_new_for_string("Skype.png", NULL)); + unity_protocol_preview_set_image_source_uri(proto_obj, "file:///home/nick/Skype.png"); unity_protocol_preview_set_title(proto_obj, app_name.str().c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle); unity_protocol_preview_set_description(proto_obj, description); - unity_protocol_preview_set_image(proto_obj, icon); unity_protocol_preview_add_action(proto_obj, "uninstall", "Uninstall", iconHint1, 0); unity_protocol_preview_add_action(proto_obj, "launch", "Launch", iconHint2, 0); unity_protocol_preview_add_info_hint(proto_obj, "time", "Total time", iconHint1, g_variant_new("s", "16 h 34miin 45sec")); @@ -293,11 +288,11 @@ int main(int argc, char **argv) nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); - nux::logging::Logger("unity").SetLogLevel(nux::logging::Warning); // The instances for the pseudo-singletons. unity::Settings settings; unity::dash::previews::Style panel_style; unity::dash::Style dash_style; + unity::ThumbnailGenerator thumbnail_generator; TestRunner *test_runner = new TestRunner (); wt = nux::CreateGUIThread(TEXT("Unity Preview"), diff --git a/dash/previews/StandaloneMoviePreview.cpp b/dash/previews/StandaloneMoviePreview.cpp new file mode 100644 index 000000000..cf4996734 --- /dev/null +++ b/dash/previews/StandaloneMoviePreview.cpp @@ -0,0 +1,250 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU 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 GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ +#include <gtk/gtk.h> + +#include "Nux/Nux.h" +#include "Nux/VLayout.h" +#include "Nux/WindowThread.h" +#include "NuxGraphics/GraphicsEngine.h" +#include <Nux/Layout.h> +#include <NuxCore/Logger.h> +#include <UnityCore/Variant.h> +#include <UnityCore/ApplicationPreview.h> +#include <unity-protocol.h> + +#include "unity-shared/FontSettings.h" +#include "unity-shared/UnitySettings.h" +#include "unity-shared/PreviewStyle.h" +#include "unity-shared/DashStyle.h" +#include "unity-shared/ThumbnailGenerator.h" + +#include "Preview.h" +#include "PreviewContainer.h" + + +#define WIDTH 972 +#define HEIGHT 452 + +using namespace unity; +using namespace unity::dash; + +class DummyView : public nux::View +{ +public: + DummyView(nux::View* view) + : View(NUX_TRACKER_LOCATION) + { + SetAcceptKeyNavFocusOnMouseDown(false); + SetAcceptKeyNavFocusOnMouseEnter(false); + + nux::ROPConfig rop; + rop.Blend = true; + rop.SrcBlend = GL_ONE; + rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; + bg_layer_.reset(new nux::ColorLayer(nux::Color(81, 26, 48), true, rop)); + + nux::Layout* layout = new nux::VLayout(); + layout->SetPadding(16); + layout->AddView(view, 1, nux::MINOR_POSITION_CENTER); + SetLayout(layout); + } + + // Keyboard navigation + bool AcceptKeyNavFocus() + { + return false; + } + +protected: + virtual void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) + { + nux::Geometry const& base = GetGeometry(); + + gfx_engine.PushClippingRectangle(base); + nux::GetPainter().PaintBackground(gfx_engine, base); + + 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); + + bg_layer_->SetGeometry(GetGeometry()); + nux::GetPainter().RenderSinglePaintLayer(gfx_engine, GetGeometry(), bg_layer_.get()); + + gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); + + gfx_engine.PopClippingRectangle(); + } + + virtual void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) + { + nux::Geometry const& base = GetGeometry(); + gfx_engine.PushClippingRectangle(base); + + if (!IsFullRedraw()) + nux::GetPainter().PushLayer(gfx_engine, GetGeometry(), bg_layer_.get()); + + if (GetCompositionLayout()) + GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); + + if (!IsFullRedraw()) + nux::GetPainter().PopBackground(); + + gfx_engine.PopClippingRectangle(); + } + + typedef std::unique_ptr<nux::AbstractPaintLayer> LayerPtr; + LayerPtr bg_layer_; +}; + +class TestRunner +{ +public: + TestRunner (); + ~TestRunner (); + + static void InitWindowThread (nux::NThread* thread, void* InitData); + void Init (); + void NavRight(); + void NavLeft(); + + previews::PreviewContainer::Ptr container_; + nux::Layout *layout_; + int nav_iter; +}; + +TestRunner::TestRunner () +{ + nav_iter = 0; +} + +TestRunner::~TestRunner () +{ +} + +void TestRunner::Init () +{ + container_ = new previews::PreviewContainer(NUX_TRACKER_LOCATION); + container_->navigate_right.connect(sigc::mem_fun(this, &TestRunner::NavRight)); + container_->navigate_left.connect(sigc::mem_fun(this, &TestRunner::NavLeft)); + container_->request_close.connect([&]() { exit(0); }); + + DummyView* dummyView = new DummyView(container_.GetPointer()); + layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); + layout_->AddView(dummyView, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); + nux::GetWindowThread()->SetLayout (layout_); + + NavRight(); +} + +void TestRunner::NavRight() +{ + std::stringstream title; + title << "Up " << ++nav_iter; + + const char* subtitle = "2009"; + const char* description = "By tying thousands of balloons to his home, 78-year-old Carl sets out to fulfill his lifelong dream to see the wilds of South America. Russell, a wilderness explorer 70 years younger, inadvertently becomes a stowaway."; + + glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_movie_preview_new())); + + unity_protocol_movie_preview_set_rating(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 0.5); + unity_protocol_movie_preview_set_num_ratings(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 17); + +// unity_protocol_preview_set_image_source_uri(proto_obj, "file:///home/nick/Videos/test-1.ogv"); + unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTMwODg0NDY1Nl5BMl5BanBnXkFtZTcwMjkwNTgyMg@@._V1._SY317_.jpg"); + unity_protocol_preview_set_title(proto_obj, title.str().c_str()); + unity_protocol_preview_set_subtitle(proto_obj, subtitle); + unity_protocol_preview_set_description(proto_obj, description); + unity_protocol_preview_add_action(proto_obj, "play", "Play", NULL, 0); + unity_protocol_preview_add_action(proto_obj, "upgradHD", "Upgrade to HD", NULL, 0); + unity_protocol_preview_add_info_hint(proto_obj, "director", "Director", NULL, g_variant_new("s", "Steve Martino, Mike Thurmeier")); + unity_protocol_preview_add_info_hint(proto_obj, "cast", "Cast", NULL, g_variant_new("s", "Ray Romano, Denis Leary and John Leguizamo")); + unity_protocol_preview_add_info_hint(proto_obj, "genre", "Genre", NULL, g_variant_new("s", "Animation")); + + glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), + glib::StealRef()); + + dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); + container_->Preview(preview_model, previews::Navigation::RIGHT); +} + +void TestRunner::NavLeft() +{ + std::stringstream title; + title << "Ice Age, Continental Drift" << --nav_iter; + + const char* subtitle = "2012, 88 min"; + const char* description = "Manny, Diego, and Sid embark upon another adventure after their continent is set adrift. Using an iceberg as a ship, they encounter sea creatures and battle pirates as they explore a new world."; + + glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_movie_preview_new())); + + unity_protocol_movie_preview_set_rating(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 0.5); + unity_protocol_movie_preview_set_num_ratings(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 17); + + unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); + unity_protocol_preview_set_title(proto_obj, title.str().c_str()); + unity_protocol_preview_set_subtitle(proto_obj, subtitle); + unity_protocol_preview_set_description(proto_obj, description); + unity_protocol_preview_add_action(proto_obj, "play", "Play", NULL, 0); + unity_protocol_preview_add_action(proto_obj, "upgradHD", "Upgrade to HD", NULL, 0); + unity_protocol_preview_add_info_hint(proto_obj, "director", "Director", NULL, g_variant_new("s", "Steve Martino, Mike Thurmeier")); + unity_protocol_preview_add_info_hint(proto_obj, "cast", "Cast", NULL, g_variant_new("s", "Ray Romano, Denis Leary and John Leguizamo")); + unity_protocol_preview_add_info_hint(proto_obj, "genre", "Genre", NULL, g_variant_new("s", "Animation")); + + glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), + glib::StealRef()); + + dash::Preview::Ptr preview_model(dash::Preview::PreviewForVariant(v)); + container_->Preview(preview_model, previews::Navigation::LEFT); + +} + +void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) +{ + TestRunner *self = (TestRunner *) InitData; + self->Init (); +} + +int main(int argc, char **argv) +{ + nux::WindowThread* wt = NULL; + + gtk_init (&argc, &argv); + + nux::NuxInitialize(0); + nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); + // The instances for the pseudo-singletons. + unity::Settings settings; + unity::dash::previews::Style panel_style; + unity::dash::Style dash_style; + unity::ThumbnailGenerator thumbnail_generator; + + TestRunner *test_runner = new TestRunner (); + wt = nux::CreateGUIThread(TEXT("Unity Preview"), + WIDTH, HEIGHT, + 0, + &TestRunner::InitWindowThread, + test_runner); + + wt->Run (NULL); + delete wt; + return 0; +} + + diff --git a/dash/previews/StandaloneMusicPreview.cpp b/dash/previews/StandaloneMusicPreview.cpp index 7981bbaa8..19c749a19 100644 --- a/dash/previews/StandaloneMusicPreview.cpp +++ b/dash/previews/StandaloneMusicPreview.cpp @@ -33,10 +33,11 @@ #include "unity-shared/UnitySettings.h" #include "unity-shared/PreviewStyle.h" #include "unity-shared/DashStyle.h" +#include "unity-shared/ThumbnailGenerator.h" #include "Preview.h" #include "PreviewContainer.h" -#include "StandaloneDBusTestRunner.h" +#include "LensDBusTestRunner.h" #define WIDTH 972 @@ -118,7 +119,7 @@ protected: LayerPtr bg_layer_; }; -class TestRunner : public previews::StandaloneDBusTestRunner +class TestRunner : public previews::LensDBusTestRunner { public: TestRunner(std::string const& search_string); @@ -138,7 +139,7 @@ public: }; TestRunner::TestRunner (std::string const& search_string) -: StandaloneDBusTestRunner("com.canonical.Unity.Lens.Music","/com/canonical/unity/lens/music") +: LensDBusTestRunner("com.canonical.Unity.Lens.Music","/com/canonical/unity/lens/music", "com.canonical.Unity.Lens") , search_string_(search_string) , first_(true) { @@ -202,10 +203,10 @@ void TestRunner::NavRight() Preview(result.uri); previews::Navigation navDisabled = previews::Navigation::BOTH; - if (nav_iter < results_->count.Get()) - navDisabled = previews::Navigation( (unsigned int)results_ & ~((unsigned int)previews::Navigation::RIGHT)); + if (nav_iter < results_->count.Get()-1) + navDisabled = previews::Navigation( (unsigned int)navDisabled & ~((unsigned int)previews::Navigation::RIGHT)); if (results_->count.Get() > 0 && nav_iter > 0) - navDisabled = previews::Navigation( (unsigned int)results_ & ~((unsigned int)previews::Navigation::LEFT)); + navDisabled = previews::Navigation( (unsigned int)navDisabled & ~((unsigned int)previews::Navigation::LEFT)); container_->DisableNavButton(navDisabled); } @@ -218,10 +219,10 @@ void TestRunner::NavLeft() Preview(result.uri); previews::Navigation navDisabled = previews::Navigation::BOTH; - if (nav_iter < results_->count.Get()) - navDisabled = previews::Navigation( (unsigned int)results_ & ~((unsigned int)previews::Navigation::RIGHT)); + if (nav_iter < results_->count.Get()-1) + navDisabled = previews::Navigation( (unsigned int)navDisabled & ~((unsigned int)previews::Navigation::RIGHT)); if (results_->count.Get() > 0 && nav_iter > 0) - navDisabled = previews::Navigation( (unsigned int)results_ & ~((unsigned int)previews::Navigation::LEFT)); + navDisabled = previews::Navigation( (unsigned int)navDisabled & ~((unsigned int)previews::Navigation::LEFT)); container_->DisableNavButton(navDisabled); } @@ -240,17 +241,17 @@ int main(int argc, char **argv) if (argc < 2) { - printf("Usage: music_previews SEARCH_STRING"); + printf("Usage: music_previews SEARCH_STRING\n"); return 1; } nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); - nux::logging::Logger("unity").SetLogLevel(nux::logging::Trace); // The instances for the pseudo-singletons. unity::Settings settings; unity::dash::previews::Style panel_style; unity::dash::Style dash_style; + unity::ThumbnailGenerator thumbnail_generator; TestRunner *test_runner = new TestRunner (argv[1]); wt = nux::CreateGUIThread(TEXT("Unity Preview"), diff --git a/dash/previews/StandaloneThumbnailer.cpp b/dash/previews/StandaloneThumbnailer.cpp new file mode 100644 index 000000000..80951083b --- /dev/null +++ b/dash/previews/StandaloneThumbnailer.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU 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 GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ +#include <gtk/gtk.h> + +#include "Nux/Nux.h" +#include "Nux/VLayout.h" +#include "Nux/SystemThread.h" +#include <Nux/Layout.h> +#include <NuxCore/Logger.h> +#include <UnityCore/Variant.h> + +#include "DBusTestRunner.h" + + +#define WIDTH 500 +#define HEIGHT 500 + +using namespace unity; +using namespace unity::dash; + +namespace +{ +nux::logging::Logger logger("unity.dash.StandaloneThumbnailer"); +} + + +class TestRunner : public previews::DBusTestRunner +{ +public: + TestRunner(std::string const& thumnail_uri); + ~TestRunner (); + + static void InitWindowThread (nux::NThread* thread, void* InitData); + void Init (); + + void OnProxyConnectionChanged(); + + void OnGetSupported(GVariant* parameters); + void OnThumbnailReady(GVariant* parameters); + void OnThumbnailStarted(GVariant* parameters); + void OnThumbnailFinished(GVariant* parameters); + void OnThumbnailError(GVariant* parameters); + + std::string thumnail_uri_; +}; + +TestRunner::TestRunner (std::string const& thumnail_uri) +: DBusTestRunner("org.freedesktop.thumbnails.Thumbnailer1","/org/freedesktop/thumbnails/Thumbnailer1", "org.freedesktop.thumbnails.Thumbnailer1") +, thumnail_uri_(thumnail_uri) +{ + proxy_->Connect("Ready", sigc::mem_fun(this, &TestRunner::OnThumbnailReady)); + proxy_->Connect("Started", sigc::mem_fun(this, &TestRunner::OnThumbnailStarted)); + proxy_->Connect("Finished", sigc::mem_fun(this, &TestRunner::OnThumbnailFinished)); + proxy_->Connect("Error", sigc::mem_fun(this, &TestRunner::OnThumbnailError)); +} + +TestRunner::~TestRunner () +{ +} + +void TestRunner::Init () +{ +} + +void TestRunner::OnProxyConnectionChanged() +{ + DBusTestRunner::OnProxyConnectionChanged(); + + if (proxy_->IsConnected()) + { + GVariantBuilder *builder_uri = g_variant_builder_new (G_VARIANT_TYPE ("as")); + g_variant_builder_add (builder_uri, "s", thumnail_uri_.c_str()); + GVariantBuilder *builder_mime = g_variant_builder_new (G_VARIANT_TYPE ("as")); + g_variant_builder_add (builder_mime, "s", "video/ogg"); + GVariant *value = g_variant_new ("(asasssu)", builder_uri, builder_mime, "xxlarge", "default", 0); + + printf("type: %s\n", g_variant_get_type_string(value)); + + g_variant_builder_unref (builder_uri); + g_variant_builder_unref (builder_mime); + + proxy_->Call("Queue", value, + sigc::mem_fun(this, &TestRunner::OnGetSupported), NULL); + } +} + +void TestRunner::OnThumbnailReady(GVariant* parameters) +{ + LOG_INFO(logger) << "Thumbnail ready"; + + GVariantIter *thumbnail_iter; + guint handle; + g_variant_get(parameters, "(uas)", &handle, &thumbnail_iter); + + gchar *str; + while (g_variant_iter_loop (thumbnail_iter, "s", &str)) + g_print ("thumbnail @ %s\n", str); + g_variant_iter_free (thumbnail_iter); +} + +void TestRunner::OnThumbnailStarted(GVariant* parameters) +{ + LOG_INFO(logger) << "Thumbnail started"; +} + +void TestRunner::OnThumbnailFinished(GVariant* parameters) +{ + LOG_INFO(logger) << "Thumbnail finished"; +} + +void TestRunner::OnThumbnailError(GVariant* parameters) +{ + GVariantIter *thumbnail_iter; + guint handle; + gint error_code; + glib::String message; + g_variant_get(parameters, "(uasis)", &handle, &thumbnail_iter, &error_code, &message); + g_variant_iter_free (thumbnail_iter); + + + LOG_INFO(logger) << "Thumbnail error: " << message; +} + +void TestRunner::OnGetSupported(GVariant* parameters) +{ + LOG_INFO(logger) << "OnGetSupported return"; + + printf("return type: %s\n", g_variant_get_type_string(parameters)); +} + + +void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) +{ + TestRunner *self = (TestRunner *) InitData; + self->Init (); +} + +int main(int argc, char **argv) +{ + nux::WindowThread* wt = NULL; + + gtk_init (&argc, &argv); + + if (argc < 2) + { + printf("Usage: thumbnailer URI"); + return 1; + } + + nux::NuxInitialize(0); + nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); + nux::logging::Logger("unity").SetLogLevel(nux::logging::Trace); + + TestRunner *test_runner = new TestRunner (argv[1]); + wt = nux::CreateGUIThread(TEXT("Unity Preview"), + WIDTH, HEIGHT, + 0, + &TestRunner::InitWindowThread, + test_runner); + + wt->Run (NULL); + delete wt; + return 0; +} + + diff --git a/dash/previews/Track.cpp b/dash/previews/Track.cpp index 54d7274d1..b29127b33 100644 --- a/dash/previews/Track.cpp +++ b/dash/previews/Track.cpp @@ -37,9 +37,24 @@ namespace dash namespace previews { +class TmpView : public nux::View +{ +public: + TmpView(NUX_FILE_LINE_PROTO): View(NUX_FILE_LINE_PARAM) {} + + void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) {} + void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) + { + if (GetCompositionLayout()) + GetCompositionLayout()->ProcessDraw(gfx_engine, force_draw); + } +}; + namespace { nux::logging::Logger logger("unity.dash.previews.track"); + +const int layout_spacing = 6; } NUX_IMPLEMENT_OBJECT_TYPE(Track); @@ -165,24 +180,21 @@ void Track::SetupViews() layout->SetLeftAndRightPadding(0,0); nux::BaseTexture* tex_play = style.GetPlayIcon(); - IconTexture* status_play = new IconTexture(tex_play, tex_play ? tex_play->GetWidth() : 25, tex_play ? tex_play->GetHeight() : 25); - status_play->mouse_click.connect([&](int, int, unsigned long, unsigned long) { play.emit(uri_); }); - status_play->mouse_enter.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseEnter)); - status_play->mouse_leave.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseLeave)); + IconTexture* status_play = new IconTexture(tex_play, style.GetStatusIconSize(), style.GetStatusIconSize()); + status_play->SetDrawMode(IconTexture::DrawMode::STRETCH_WITH_ASPECT); + status_play->SetInputEventSensitivity(false); nux::BaseTexture* tex_pause = style.GetPauseIcon(); - IconTexture* status_pause = new IconTexture(tex_pause, tex_pause ? tex_pause->GetWidth() : 25, tex_pause ? tex_pause->GetHeight() : 25); - status_pause->mouse_click.connect([&](int, int, unsigned long, unsigned long) { pause.emit(uri_); }); - status_pause->mouse_enter.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseEnter)); - status_pause->mouse_leave.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseLeave)); + IconTexture* status_pause = new IconTexture(tex_pause, style.GetStatusIconSize(), style.GetStatusIconSize()); + status_pause->SetDrawMode(IconTexture::DrawMode::STRETCH_WITH_ASPECT); + status_pause->SetInputEventSensitivity(false); track_number_ = new nux::StaticCairoText("", NUX_TRACKER_LOCATION); track_number_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_CENTRE); track_number_->SetTextVerticalAlignment(nux::StaticCairoText::NUX_ALIGN_CENTRE); track_number_->SetLines(-1); track_number_->SetFont(style.track_font()); - track_number_->mouse_enter.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseEnter)); - track_number_->mouse_leave.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseLeave)); + track_number_->SetInputEventSensitivity(false); title_ = new nux::StaticCairoText("", NUX_TRACKER_LOCATION); title_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_LEFT); @@ -196,22 +208,35 @@ void Track::SetupViews() duration_->SetTextVerticalAlignment(nux::StaticCairoText::NUX_ALIGN_CENTRE); duration_->SetLines(-1); duration_->SetFont(style.track_font()); + duration_->SetMaximumWidth(style.GetMusicDurationWidth()); + duration_->SetMaximumWidth(style.GetMusicDurationWidth()); // Layouts // stick text fields in a layout so they don't alter thier geometry. - status_play_layout_ = new nux::HLayout(); - status_play_layout_->AddSpace(0, 1); - status_play_layout_->AddView(status_play, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); - status_play_layout_->AddSpace(0, 1); - - status_pause_layout_ = new nux::HLayout(); - status_pause_layout_->AddSpace(0, 1); - status_pause_layout_->AddView(status_pause, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); - status_pause_layout_->AddSpace(0, 1); - - track_number_layout_ = new nux::HLayout(); - track_number_layout_->AddSpace(0, 1); - track_number_layout_->AddView(track_number_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); - track_number_layout_->AddSpace(0, 1); + status_play_layout_ = new TmpView(); + status_play_layout_->SetLayout(new nux::HLayout()); + status_play_layout_->GetLayout()->AddSpace(0, 1); + status_play_layout_->GetLayout()->AddView(status_play, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); + status_play_layout_->GetLayout()->AddSpace(0, 1); + status_play_layout_->mouse_click.connect([&](int, int, unsigned long, unsigned long) { play.emit(uri_); }); + status_play_layout_->mouse_enter.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseEnter)); + status_play_layout_->mouse_leave.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseLeave)); + + status_pause_layout_ = new TmpView(); + status_pause_layout_->SetLayout(new nux::HLayout()); + status_pause_layout_->GetLayout()->AddSpace(0, 1); + status_pause_layout_->GetLayout()->AddView(status_pause, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); + status_pause_layout_->GetLayout()->AddSpace(0, 1); + status_pause_layout_->mouse_click.connect([&](int, int, unsigned long, unsigned long) { pause.emit(uri_); }); + status_pause_layout_->mouse_enter.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseEnter)); + status_pause_layout_->mouse_leave.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseLeave)); + + track_number_layout_ = new TmpView(); + track_number_layout_->SetLayout(new nux::HLayout()); + track_number_layout_->GetLayout()->AddSpace(0, 1); + track_number_layout_->GetLayout()->AddView(track_number_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); + track_number_layout_->GetLayout()->AddSpace(0, 1); + track_number_layout_->mouse_enter.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseEnter)); + track_number_layout_->mouse_leave.connect(sigc::mem_fun(this, &Track::OnTrackControlMouseLeave)); track_status_layout_ = new nux::LayeredLayout(); track_status_layout_->AddLayer(status_play_layout_, true); @@ -351,16 +376,13 @@ void Track::UpdateTrackState() long Track::ComputeContentSize() { - track_status_layout_->SetMinimumWidth(30); - track_status_layout_->SetMaximumWidth(30); - - track_number_->SetMinimumWidth(30); - track_number_->SetMaximumWidth(30); + previews::Style& style = previews::Style::Instance(); + nux::Geometry const& geo = GetGeometry(); - duration_->SetMaximumWidth(40); - duration_->SetMaximumWidth(40); + track_status_layout_->SetMinimumWidth(geo.height); + track_status_layout_->SetMaximumWidth(geo.height); - title_->SetMaximumWidth(GetGeometry().width - 30 - 30 - 12); + title_->SetMaximumWidth(GetGeometry().width - geo.height - style.GetMusicDurationWidth() - layout_spacing*2); return View::ComputeContentSize(); } diff --git a/dash/previews/Track.h b/dash/previews/Track.h index b14df2057..4a77f0384 100644 --- a/dash/previews/Track.h +++ b/dash/previews/Track.h @@ -89,9 +89,9 @@ protected: nux::Layout* title_layout_; nux::Layout* duration_layout_; - nux::Layout* status_play_layout_; - nux::Layout* status_pause_layout_; - nux::Layout* track_number_layout_; + nux::View* status_play_layout_; + nux::View* status_pause_layout_; + nux::View* track_number_layout_; nux::LayeredLayout* track_status_layout_; dash::PlayState play_state_; diff --git a/hud/HudButton.cpp b/hud/HudButton.cpp index 435ae0126..13159eba7 100644 --- a/hud/HudButton.cpp +++ b/hud/HudButton.cpp @@ -101,9 +101,8 @@ void HudButton::InitTheme() void HudButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) { - int font_size = 17; dash::Style::Instance().SquareButton(cr, faked_state, "", - is_rounded, font_size, + is_rounded, 17, dash::Alignment::LEFT, true); } diff --git a/plugins/unityshell/resources/preview_next.svg b/plugins/unityshell/resources/preview_next.svg new file mode 100644 index 000000000..75565f866 --- /dev/null +++ b/plugins/unityshell/resources/preview_next.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="14px" height="21px" viewBox="0 0 14 21" enable-background="new 0 0 14 21" xml:space="preserve"> +<g> + <g> + <path fill="#FFFFFF" d="M13.998,10.5c0.001,0.001,0.001,0.002,0.002,0.002V10.5C13.999,10.5,13.999,10.5,13.998,10.5z M8.139,5.49 + C7.364,4.623,6.623,3.718,5.913,2.774S4.576,0.905,4.034,0H0c0.543,0.905,1.208,1.882,1.995,2.929 + c0.787,1.048,1.622,2.082,2.502,3.104C5.38,7.055,6.275,8.005,7.185,8.885C7.812,9.493,8.417,10.028,9,10.5 + c-0.583,0.473-1.188,1.009-1.815,1.616c-0.909,0.88-1.805,1.83-2.688,2.852c-0.88,1.022-1.715,2.057-2.502,3.104 + C1.208,19.12,0.543,20.095,0,21h4.034c0.542-0.905,1.168-1.829,1.878-2.773s1.451-1.85,2.226-2.717 + c0.775-0.866,1.562-1.661,2.361-2.385c0.688-0.621,2.531-1.949,3.498-2.625c-0.967-0.675-2.81-2.002-3.498-2.624 + C9.701,7.152,8.914,6.357,8.139,5.49z"/> + </g> +</g> +</svg> diff --git a/plugins/unityshell/resources/preview_pause.svg b/plugins/unityshell/resources/preview_pause.svg new file mode 100644 index 000000000..dade7bb60 --- /dev/null +++ b/plugins/unityshell/resources/preview_pause.svg @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="7px" height="9px" viewBox="0 0 7 9" enable-background="new 0 0 7 9" xml:space="preserve"> +<rect fill="#FFFFFF" width="2" height="9"/> +<rect x="5" fill="#FFFFFF" width="2" height="9"/> +</svg> diff --git a/plugins/unityshell/resources/preview_play.svg b/plugins/unityshell/resources/preview_play.svg new file mode 100644 index 000000000..e1d61bf70 --- /dev/null +++ b/plugins/unityshell/resources/preview_play.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="5px" height="7px" viewBox="0 0 5 7" enable-background="new 0 0 5 7" xml:space="preserve"> +<path fill="#FFFFFF" d="M0,7c0.343-0.184,0.725-0.41,1.145-0.677c0.421-0.268,0.853-0.562,1.296-0.878 + c0.445-0.318,0.885-0.645,1.322-0.979C4.197,4.132,4.61,3.805,5,3.487C4.61,3.169,4.193,2.843,3.751,2.509 + C3.307,2.174,2.862,1.852,2.418,1.542C1.975,1.233,1.542,0.945,1.122,0.677C0.701,0.409,0.328,0.184,0,0V7z"/> +</svg> diff --git a/plugins/unityshell/resources/preview_previous.svg b/plugins/unityshell/resources/preview_previous.svg new file mode 100644 index 000000000..70cca99e0 --- /dev/null +++ b/plugins/unityshell/resources/preview_previous.svg @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="14px" height="21px" viewBox="0 0 14 21" enable-background="new 0 0 14 21" xml:space="preserve"> +<g> + <g> + <path fill="#FFFFFF" d="M633,400.501c0.001,0,0.001-0.001,0.002-0.001c-0.001,0-0.001-0.001-0.002-0.001V400.501z + M642.503,404.967c-0.883-1.021-1.778-1.972-2.687-2.852c-0.629-0.607-1.233-1.143-1.816-1.615 + c0.583-0.473,1.188-1.008,1.816-1.615c0.908-0.88,1.804-1.83,2.687-2.852c0.882-1.022,1.716-2.057,2.503-3.104 + c0.786-1.049,1.451-2.025,1.994-2.93h-4.034c-0.542,0.904-1.168,1.829-1.878,2.773c-0.711,0.944-1.452,1.85-2.227,2.717 + c-0.775,0.866-1.562,1.661-2.362,2.386c-0.687,0.622-2.53,1.948-3.497,2.624c0.967,0.676,2.811,2.002,3.497,2.624 + c0.8,0.724,1.587,1.52,2.362,2.387c0.774,0.866,1.516,1.771,2.227,2.716c0.71,0.943,1.336,1.868,1.878,2.773H647 + c-0.543-0.905-1.208-1.882-1.994-2.929C644.219,407.023,643.385,405.989,642.503,404.967z"/> + </g> +</g> +<g> + <g> + <path fill="#FFFFFF" d="M0,10.501c0.001,0,0.001-0.001,0.002-0.001c-0.001,0-0.001-0.001-0.002-0.001V10.501z M9.503,14.967 + c-0.883-1.021-1.778-1.972-2.687-2.852C6.188,11.508,5.583,10.973,5,10.5c0.583-0.473,1.188-1.008,1.816-1.615 + c0.909-0.88,1.804-1.83,2.687-2.852c0.882-1.022,1.716-2.056,2.503-3.104C12.792,1.881,13.457,0.905,14,0H9.966 + c-0.542,0.905-1.168,1.83-1.878,2.774C7.377,3.718,6.636,4.623,5.861,5.49C5.086,6.356,4.299,7.151,3.499,7.876 + C2.812,8.498,0.969,9.824,0.002,10.5c0.967,0.676,2.81,2.002,3.497,2.624c0.8,0.724,1.587,1.52,2.362,2.387 + c0.774,0.866,1.516,1.771,2.227,2.716C8.798,19.17,9.424,20.095,9.966,21H14c-0.543-0.905-1.208-1.882-1.994-2.929 + C11.219,17.023,10.385,15.989,9.503,14.967z"/> + </g> +</g> +</svg> diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index 765ad980b..8bc218263 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -52,6 +52,7 @@ #include "UBusWrapper.h" #include "UnityshellPrivate.h" #include "UnityShowdesktopHandler.h" +#include "ThumbnailGenerator.h" #ifndef USE_MODERN_COMPIZ_GL #include "ScreenEffectFramebufferObject.h" #endif @@ -248,6 +249,7 @@ private: panel::Style panel_style_; FontSettings font_settings_; internal::FavoriteStoreGSettings favorite_store_; + ThumbnailGenerator thumbnail_generator_; /* The window thread should be the last thing removed, as c++ does it in reverse order */ std::unique_ptr<nux::WindowThread> wt; diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index 96cb50ffe..d1b06f000 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -35,6 +35,7 @@ set (UNITY_SHARED_SOURCES CoverArt.cpp BackgroundEffectHelper.cpp DashStyle.cpp + DefaultThumbnailProvider.cpp FontSettings.cpp KeyboardUtil.cpp IMTextEntry.cpp @@ -55,12 +56,15 @@ set (UNITY_SHARED_SOURCES SearchBarSpinner.cpp StaticCairoText.cpp TextureCache.cpp + TextureThumbnailProvider.cpp + ThumbnailGenerator.cpp Timer.cpp UBusWrapper.cpp UScreen.cpp UnitySettings.cpp UnityWindowStyle.cpp UnityWindowView.cpp + UserThumbnailProvider.cpp WindowManager.cpp ubus-server.cpp ) diff --git a/unity-shared/CoverArt.cpp b/unity-shared/CoverArt.cpp index c5c2caa53..af5dff168 100644 --- a/unity-shared/CoverArt.cpp +++ b/unity-shared/CoverArt.cpp @@ -23,8 +23,11 @@ #include "CoverArt.h" #include "unity-shared/IntrospectableWrappers.h" +#include "unity-shared/CairoTexture.h" #include <NuxCore/Logger.h> #include <Nux/VLayout.h> +#include "DashStyle.h" +#include "IconLoader.h" namespace unity { @@ -36,6 +39,8 @@ namespace previews namespace { nux::logging::Logger logger("unity.dash.previews.coverart"); + +const int icon_width = 256; } NUX_IMPLEMENT_OBJECT_TYPE(CoverArt); @@ -43,6 +48,11 @@ NUX_IMPLEMENT_OBJECT_TYPE(CoverArt); CoverArt::CoverArt() : View(NUX_TRACKER_LOCATION) , overlay_text_(nullptr) + , thumb_handle_(0) + , slot_handle_(0) + , stretch_image_(false) + , waiting_(false) + , rotation_(0.0) { SetupViews(); } @@ -51,47 +61,221 @@ CoverArt::~CoverArt() { if (overlay_text_) overlay_text_->UnReference(); + + if (slot_handle_ > 0) + { + IconLoader::GetDefault().DisconnectHandle(slot_handle_); + slot_handle_ = 0; + } + + if (notifier_) + notifier_->Cancel(); } void CoverArt::SetImage(std::string const& image_hint) -{ - if (overlay_text_ && GetLayout()) +{ + spinner_timeout_.reset(); + frame_timeout_.reset(); + waiting_ = false; + + if (slot_handle_ > 0) + { + IconLoader::GetDefault().DisconnectHandle(slot_handle_); + slot_handle_ = 0; + } + + if (GetLayout()) GetLayout()->RemoveChildObject(overlay_text_); - texture_screenshot_.Adopt(nux::CreateTexture2DFromFile(image_hint.c_str(), -1, true)); + GIcon* icon = g_icon_new_for_string(image_hint.c_str(), NULL); - if (!texture_screenshot_ && GetLayout()) + glib::Object<GFile> image_file; + if (g_strrstr(image_hint.c_str(), "://")) { - GetLayout()->AddView(overlay_text_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0, nux::LayoutPosition(1)); + /* try to open the source file for reading */ + image_file = g_file_new_for_uri (image_hint.c_str()); + } + else + { + image_file = g_file_new_for_path (image_hint.c_str()); } + + if (g_file_query_exists(image_file, NULL)) + { + // for files on disk, we stretch to maximum aspect ratio. + stretch_image_ = true; + + GFileInputStream *stream; + GError *error = NULL; + + stream = g_file_read (image_file, NULL, &error); + + if (error != NULL) + { + g_error_free (error); + + if (icon != NULL) + g_object_unref(icon); + return; + } + + /* stream image into pixel-buffer. */ + glib::Object<GdkPixbuf> pixbuf(gdk_pixbuf_new_from_stream (G_INPUT_STREAM (stream), NULL, &error)); + g_object_unref (stream); + + texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true)); + + if (!texture_screenshot_ && GetLayout()) + { + GetLayout()->AddView(overlay_text_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0, nux::LayoutPosition(1)); + ComputeContentSize(); + } + QueueDraw(); + } + else if (G_IS_ICON(icon)) + { + StartWaiting(); + slot_handle_ = IconLoader::GetDefault().LoadFromGIconString(image_hint, icon_width, sigc::mem_fun(this, &CoverArt::IconLoaded)); + } + else + { + StartWaiting(); + slot_handle_ = IconLoader::GetDefault().LoadFromIconName(image_hint, icon_width, sigc::mem_fun(this, &CoverArt::IconLoaded)); + } + + if (icon != NULL) + g_object_unref(icon); +} + +void CoverArt::GenerateImage(std::string const& uri) +{ + StartWaiting(); + notifier_ = ThumbnailGenerator::Instance().GetThumbnail(uri, 512); + + notifier_->ready.connect(sigc::mem_fun(this, &CoverArt::OnThumbnailGenerated)); + notifier_->error.connect(sigc::mem_fun(this, &CoverArt::OnThumbnailError)); +} + +void CoverArt::StartWaiting() +{ + if (waiting_) + return; + + waiting_ = true; + + rotate_matrix_.Rotate_z(0.0f); + rotation_ = 0.0f; + + spinner_timeout_.reset(new glib::TimeoutSeconds(5, [&] + { + texture_screenshot_.Release(); + waiting_ = false; + + if (GetLayout()) + { + GetLayout()->RemoveChildObject(overlay_text_); + GetLayout()->AddView(overlay_text_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0, nux::LayoutPosition(1)); + ComputeContentSize(); + } + + QueueDraw(); + return false; + })); + QueueDraw(); } -void CoverArt::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) +void CoverArt::IconLoaded(std::string const& texid, unsigned size, glib::Object<GdkPixbuf> const& pixbuf) { - nux::Geometry const& base = GetGeometry(); + // Finished waiting + spinner_timeout_.reset(); + frame_timeout_.reset(); + waiting_ = false; + + stretch_image_ = false; - gfx_engine.PushClippingRectangle(base); - nux::GetPainter().PaintBackground(gfx_engine, base); + int height = size; - if (texture_screenshot_) + int pixbuf_width, pixbuf_height; + pixbuf_width = gdk_pixbuf_get_width(pixbuf); + pixbuf_height = gdk_pixbuf_get_height(pixbuf); + if (G_UNLIKELY(!pixbuf_height || !pixbuf_width)) { - nux::Geometry imageDest = base; - nux::TexCoordXForm texxform; + pixbuf_width = (pixbuf_width) ? pixbuf_width : 1; // no zeros please + pixbuf_height = (pixbuf_height) ? pixbuf_height: 1; // no zeros please + } + + if (pixbuf_width == pixbuf_height) + { + // quick path for square icons + texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true)); + QueueDraw(); + } + else + { + // slow path for non square icons that must be resized to fit in the square + // texture - float base_apsect = float(base.GetWidth()) / base.GetHeight(); - float image_aspect = float(texture_screenshot_->GetWidth()) / texture_screenshot_->GetHeight(); + float aspect = static_cast<float>(pixbuf_height) / pixbuf_width; // already sanitized width/height so can not be 0.0 + if (aspect < 1.0f) + { + pixbuf_width = icon_width; + pixbuf_height = pixbuf_width * aspect; - if (image_aspect > base_apsect) + if (pixbuf_height > height) + { + // scaled too big, scale down + pixbuf_height = height; + pixbuf_width = pixbuf_height / aspect; + } + } + else { - imageDest.SetHeight(float(imageDest.GetWidth())/image_aspect); - } - if (image_aspect < base_apsect) + pixbuf_height = height; + pixbuf_width = pixbuf_height / aspect; + } + + if (gdk_pixbuf_get_height(pixbuf) == pixbuf_height) { - imageDest.SetWidth(image_aspect*imageDest.GetHeight()); + // we changed our mind, fast path is good + texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true)); + QueueDraw(); + return; } - int border_width = 1; + nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, pixbuf_width, pixbuf_height); + cairo_t* cr = cairo_graphics.GetInternalContext(); + + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_paint(cr); + + float scale = float(pixbuf_height) / gdk_pixbuf_get_height(pixbuf); + + //cairo_translate(cr, + // static_cast<int>((width - (pixbuf_width * scale)) * 0.5), + // static_cast<int>((height - (pixbuf_height * scale)) * 0.5)); + + cairo_scale(cr, scale, scale); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); + cairo_paint(cr); + + texture_screenshot_.Adopt(texture_from_cairo_graphics(cairo_graphics)); + QueueDraw(); + } +} + +void CoverArt::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) +{ + nux::Geometry const& base = GetGeometry(); + + gfx_engine.PushClippingRectangle(base); + nux::GetPainter().PaintBackground(gfx_engine, base); + + unsigned int alpha, src, dest = 0; + gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); + gfx_engine.GetRenderStates().SetBlend(true); gfx_engine.QRP_Color(base.x, base.y, @@ -99,6 +283,31 @@ void CoverArt::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) base.GetHeight(), nux::Color(0.03f, 0.03f, 0.03f, 0.0f)); + if (texture_screenshot_) + { + nux::Geometry imageDest = base; + nux::TexCoordXForm texxform; + + if (stretch_image_ || base.GetWidth() < texture_screenshot_->GetWidth() || base.height < texture_screenshot_->GetHeight()) + { + float base_apsect = float(base.GetWidth()) / base.GetHeight(); + float image_aspect = float(texture_screenshot_->GetWidth()) / texture_screenshot_->GetHeight(); + + if (image_aspect > base_apsect) + { + imageDest.SetHeight(float(imageDest.GetWidth()) / image_aspect); + } + if (image_aspect < base_apsect) + { + imageDest.SetWidth(image_aspect * imageDest.GetHeight()); + } + } + else + { + imageDest = nux::Geometry(0, 0, texture_screenshot_->GetWidth(), texture_screenshot_->GetHeight()); + } + + texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_SCALE_COORD); texxform.SetWrap(nux::TEXWRAP_CLAMP_TO_BORDER, nux::TEXWRAP_CLAMP_TO_BORDER); texxform.SetFilter(nux::TEXFILTER_LINEAR, nux::TEXFILTER_LINEAR); @@ -108,35 +317,60 @@ void CoverArt::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) texxform.u1 = imageDest.width; texxform.v1 = imageDest.height; - gfx_engine.QRP_1Tex(imageDest.x + (float(base.GetWidth() - imageDest.GetWidth()) / 2) + border_width, - imageDest.y + (float(base.GetHeight() - imageDest.GetHeight()) / 2) + border_width, - imageDest.width - (border_width * 2), - imageDest.height - (border_width * 2), + gfx_engine.QRP_1Tex(base.x + (float(base.GetWidth() - imageDest.GetWidth()) / 2), + base.y + (float(base.GetHeight() - imageDest.GetHeight()) / 2), + imageDest.width, + imageDest.height, texture_screenshot_.GetPointer()->GetDeviceTexture(), texxform, nux::color::White); } else { - unsigned int alpha, src, dest = 0; - gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); - gfx_engine.GetRenderStates().SetBlend(true); + if (waiting_) + { + nux::TexCoordXForm texxform; + 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; - gfx_engine.QRP_Color(base.x, - base.y, - base.GetWidth(), - base.GetHeight(), - nux::Color(0.03f, 0.03f, 0.03f, 0.0f)); + nux::Geometry spin_geo(base.x + ((base.width - spin_->GetWidth()) / 2), + base.y + ((base.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 = !(base.width % 2) ? 0 : 1; + int spin_offset_h = !(base.height % 2) ? 0 : 1; - gPainter.Paint2DQuadWireframe(gfx_engine, - base.x+1, - base.y, - base.GetWidth(), - base.GetHeight(), - nux::Color(0.5f, 0.5, 0.5, 0.15f)); + gfx_engine.PushModelViewMatrix(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)); + gfx_engine.PushModelViewMatrix(rotate_matrix_); + gfx_engine.PushModelViewMatrix(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)); + + gfx_engine.QRP_1Tex(spin_geo.x, + spin_geo.y, + spin_geo.width, + spin_geo.height, + spin_->GetDeviceTexture(), + texxform, + nux::color::White); - gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); + gfx_engine.PopModelViewMatrix(); + gfx_engine.PopModelViewMatrix(); + gfx_engine.PopModelViewMatrix(); + + if (!frame_timeout_) + { + frame_timeout_.reset(new glib::Timeout(22, sigc::mem_fun(this, &CoverArt::OnFrameTimeout))); + } + } } + + gfx_engine.GetRenderStates().SetBlend(alpha, src, dest); gfx_engine.PopClippingRectangle(); } @@ -170,6 +404,13 @@ void CoverArt::SetupViews() overlay_text_->SetFont("Ubuntu 14"); overlay_text_->SetLines(-3); overlay_text_->SetText("No Image Available"); + + dash::Style& style = dash::Style::Instance(); + spin_ = style.GetSearchSpinIcon(); + + rotate_matrix_.Identity(); + rotate_matrix_.Rotate_z(0.0); + } void CoverArt::SetFont(std::string const& font) @@ -177,6 +418,45 @@ void CoverArt::SetFont(std::string const& font) overlay_text_->SetFont(font); } +void CoverArt::OnThumbnailGenerated(std::string const& uri) +{ + SetImage(uri); + notifier_.Release(); +} + +void CoverArt::OnThumbnailError(std::string const& error_hint) +{ + LOG_WARNING(logger) << "Failed to generate thumbnail: " << error_hint; + spinner_timeout_.reset(); + frame_timeout_.reset(); + waiting_ = false; + + texture_screenshot_.Release(); + if (GetLayout()) + { + GetLayout()->RemoveChildObject(overlay_text_); + GetLayout()->AddView(overlay_text_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL, 100.0, nux::LayoutPosition(1)); + ComputeContentSize(); + } + QueueDraw(); + notifier_.Release(); +} + + +bool CoverArt::OnFrameTimeout() +{ + rotation_ += 0.1f; + + if (rotation_ >= 360.0f) + rotation_ = 0.0f; + + rotate_matrix_.Rotate_z(rotation_); + QueueDraw(); + + frame_timeout_.reset(); + return false; +} + } } } \ No newline at end of file diff --git a/unity-shared/CoverArt.h b/unity-shared/CoverArt.h index dad9969ae..a4a61b84c 100644 --- a/unity-shared/CoverArt.h +++ b/unity-shared/CoverArt.h @@ -27,8 +27,10 @@ #include <Nux/Nux.h> #include <Nux/View.h> #include <UnityCore/ApplicationPreview.h> -#include "unity-shared/StaticCairoText.h" +#include <UnityCore/GLibSource.h> #include <NuxCore/ObjectPtr.h> +#include "unity-shared/StaticCairoText.h" +#include "ThumbnailGenerator.h" namespace unity { @@ -46,7 +48,10 @@ public: CoverArt(); virtual ~CoverArt(); + // Use for setting an image which is already an image (path to iamge, gicon). void SetImage(std::string const& image_hint); + // Use for generating an image for a uri which is not necessarily an image. + void GenerateImage(std::string const& uri); // From debug::Introspectable std::string GetName() const; @@ -59,9 +64,31 @@ protected: void SetupViews(); + void OnThumbnailGenerated(std::string const& uri); + void OnThumbnailError(std::string const& error_hint); + bool OnFrameTimeout(); + + void IconLoaded(std::string const& texid, unsigned size, glib::Object<GdkPixbuf> const& pixbuf); + + void StartWaiting(); + private: nux::ObjectPtr<nux::BaseTexture> texture_screenshot_; nux::StaticCairoText* overlay_text_; + + std::string image_hint_; + unsigned int thumb_handle_; + int slot_handle_; + bool stretch_image_; + ThumbnailNotifier::Ptr notifier_; + + // Spinner + bool waiting_; + nux::BaseTexture* spin_; + glib::Source::UniquePtr spinner_timeout_; + glib::Source::UniquePtr frame_timeout_; + nux::Matrix4 rotate_matrix_; + float rotation_; }; } diff --git a/unity-shared/DashStyle.cpp b/unity-shared/DashStyle.cpp index 218ba11d9..8ac12c7d7 100644 --- a/unity-shared/DashStyle.cpp +++ b/unity-shared/DashStyle.cpp @@ -123,7 +123,7 @@ public: void Text(cairo_t* cr, nux::Color const& color, std::string const& label, - int& font_size, + int font_size, double horizMargin = 4.0, Alignment alignment = Alignment::CENTER); @@ -1317,7 +1317,7 @@ void Style::Impl::GetTextExtents(int& width, void Style::Impl::Text(cairo_t* cr, nux::Color const& color, std::string const& label, - int& text_size, + int text_size, double horizMargin, Alignment alignment) { @@ -1534,7 +1534,7 @@ void Style::Impl::DrawOverlay(cairo_t* cr, } bool Style::Button(cairo_t* cr, nux::ButtonVisualState state, - std::string const& label, int& font_size, + std::string const& label, int font_size, Alignment alignment, bool zeromargin) { // sanity checks @@ -1628,10 +1628,9 @@ nux::AbstractPaintLayer* Style::FocusOverlay(int width, int height) bool Style::SquareButton(cairo_t* cr, nux::ButtonVisualState state, std::string const& label, bool curve_bottom, - int& font_size, Alignment alignment, + int font_size, Alignment alignment, bool zeromargin) { - // sanity checks if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) return false; @@ -1839,11 +1838,10 @@ bool Style::MultiRangeSegment(cairo_t* cr, } cairo_set_source_rgba(cr, pimpl->button_label_border_color_[state]); cairo_stroke(cr); - int label_font_size = 10; pimpl->Text(cr, pimpl->button_label_text_color_[state], label, - label_font_size); // 13px = 10pt + 10); // 13px = 10pt return true; } diff --git a/unity-shared/DashStyle.h b/unity-shared/DashStyle.h index 977e5a19f..440914d7d 100644 --- a/unity-shared/DashStyle.h +++ b/unity-shared/DashStyle.h @@ -92,13 +92,13 @@ public: static Style& Instance(); virtual bool Button(cairo_t* cr, nux::ButtonVisualState state, - std::string const& label, int& font_size, + std::string const& label, int font_size=-1, Alignment alignment = Alignment::CENTER, bool zeromargin=false); virtual bool SquareButton(cairo_t* cr, nux::ButtonVisualState state, std::string const& label, bool curve_bottom, - int& font_size, + int font_size=-1, Alignment alignment = Alignment::CENTER, bool zeromargin=false); diff --git a/unity-shared/DefaultThumbnailProvider.cpp b/unity-shared/DefaultThumbnailProvider.cpp new file mode 100644 index 000000000..7545b21e5 --- /dev/null +++ b/unity-shared/DefaultThumbnailProvider.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2011 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 + * <http://www.gnu.org/licenses/> + ** + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#include <Nux/Nux.h> +#include "ThumbnailGenerator.h" +#include "UnityCore/GLibWrapper.h" +#include "DefaultThumbnailProvider.h" + +namespace unity +{ + +namespace DefaultThumbnailProvider +{ + +class DefaultThumbnailer : public Thumbnailer +{ +public: + DefaultThumbnailer(std::string const& name) + : name(name) + {} + + std::string name; + + virtual std::string GetName() const { return name; } + + virtual bool Run(int size, std::string const& input_file, std::string& output_file, std::string& error_hint); +}; + + +bool DefaultThumbnailer::Run(int size, std::string const& input_file, std::string& output_file, std::string& error_hint) +{ + glib::Object<GFile> file(::g_file_new_for_uri(input_file.c_str())); + + GError *err = NULL; + glib::Object<GFileInfo> file_info(g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, NULL, &err)); + if (err != NULL) + { + error_hint = err->message; + g_error_free (err); + return ""; + } + + GIcon* icon = g_file_info_get_icon(file_info); // [transfer none] + output_file = g_icon_to_string(icon); + + return true; +} + +void Initialise() +{ + Thumbnailer::Ptr thumbnailer(new DefaultThumbnailer("default")); + std::list<std::string> mime_types; + mime_types.push_back("*"); + ThumbnailGenerator::RegisterThumbnailer(mime_types, thumbnailer); +} + +} // namespace DefaultThumbnailProvider +} // namespace unity + + diff --git a/unity-shared/DefaultThumbnailProvider.h b/unity-shared/DefaultThumbnailProvider.h new file mode 100644 index 000000000..3c7707b14 --- /dev/null +++ b/unity-shared/DefaultThumbnailProvider.h @@ -0,0 +1,38 @@ +/* + * Copyright 2011 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 + * <http://www.gnu.org/licenses/> + ** + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#ifndef UNITYSHARED_DEFAULTTHUMBNAILPROVIDER_H +#define UNITYSHARED_DEFAULTTHUMBNAILPROVIDER_H + +#include <Nux/Nux.h> + +namespace unity +{ + +namespace DefaultThumbnailProvider +{ + void Initialise(); +}; + +} // namespace unity + +#endif // UNITYSHARED_DEFAULTTHUMBNAILPROVIDER_H + diff --git a/unity-shared/IconTexture.cpp b/unity-shared/IconTexture.cpp index f97e5eb43..c324a2e29 100644 --- a/unity-shared/IconTexture.cpp +++ b/unity-shared/IconTexture.cpp @@ -51,7 +51,8 @@ IconTexture::IconTexture(nux::BaseTexture* texture, guint width, guint height) _texture_size(width, height), _loading(false), _opacity(1.0f), - _handle(0) + _handle(0), + _draw_mode(DrawMode::NORMAL) { SetMinMaxSize(width, height); } @@ -63,7 +64,8 @@ IconTexture::IconTexture(std::string const& icon_name, unsigned int size, bool d _size(size), _loading(false), _opacity(1.0f), - _handle(0) + _handle(0), + _draw_mode(DrawMode::NORMAL) { if (!icon_name.empty () && !defer_icon_loading) LoadIcon(); @@ -187,16 +189,60 @@ void IconTexture::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Color col(1.0f * _opacity, 1.0f * _opacity, 1.0f * _opacity, _opacity); nux::TexCoordXForm texxform; - texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); - texxform.SetWrap(nux::TEXWRAP_CLAMP_TO_BORDER, nux::TEXWRAP_CLAMP_TO_BORDER); - - GfxContext.QRP_1Tex(geo.x + ((geo.width - _texture_size.width) / 2), - geo.y + ((geo.height - _texture_size.height) / 2), - _texture_size.width, - _texture_size.height, - _texture_cached->GetDeviceTexture(), - texxform, - col); + + if (_draw_mode == DrawMode::STRETCH_WITH_ASPECT) + { + nux::Geometry imageDest = geo; + + float geo_apsect = float(geo.GetWidth()) / geo.GetHeight(); + float image_aspect = float(_texture_cached->GetWidth()) / _texture_cached->GetHeight(); + + if (image_aspect > geo_apsect) + { + imageDest.SetHeight(float(imageDest.GetWidth()) / image_aspect); + } + if (image_aspect < geo_apsect) + { + imageDest.SetWidth(image_aspect * imageDest.GetHeight()); + } + else + { + imageDest = nux::Geometry(0, 0, _texture_cached->GetWidth(), _texture_cached->GetHeight()); + } + + texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_SCALE_COORD); + texxform.SetWrap(nux::TEXWRAP_CLAMP_TO_BORDER, nux::TEXWRAP_CLAMP_TO_BORDER); + texxform.SetFilter(nux::TEXFILTER_LINEAR, nux::TEXFILTER_LINEAR); + + texxform.u0 = 0; + texxform.v0 = 0; + texxform.u1 = imageDest.width; + texxform.v1 = imageDest.height; + + int border_width = 1; + GfxContext.QRP_1Tex(geo.x + (float(geo.GetWidth() - imageDest.GetWidth()) / 2) + border_width, + geo.y + (float(geo.GetHeight() - imageDest.GetHeight()) / 2) + border_width, + imageDest.width - (border_width * 2), + imageDest.height - (border_width * 2), + _texture_cached.GetPointer()->GetDeviceTexture(), + texxform, + nux::color::White); + } + else + { + texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); + texxform.SetWrap(nux::TEXWRAP_CLAMP_TO_BORDER, nux::TEXWRAP_CLAMP_TO_BORDER); + + GfxContext.QRP_1Tex(geo.x + ((geo.width - _texture_size.width) / 2), + geo.y + ((geo.height - _texture_size.height) / 2), + _texture_size.width, + _texture_size.height, + _texture_cached->GetDeviceTexture(), + texxform, + col); + } + + } GfxContext.PopClippingRectangle(); @@ -259,4 +305,9 @@ bool IconTexture::AcceptKeyNavFocus() return _accept_key_nav_focus; } +void IconTexture::SetDrawMode(DrawMode mode) +{ + _draw_mode = mode; + QueueDraw(); +} } diff --git a/unity-shared/IconTexture.h b/unity-shared/IconTexture.h index 88428733c..83364a1f7 100644 --- a/unity-shared/IconTexture.h +++ b/unity-shared/IconTexture.h @@ -54,6 +54,13 @@ public: nux::BaseTexture* texture(); + enum class DrawMode + { + NORMAL, + STRETCH_WITH_ASPECT + }; + void SetDrawMode(DrawMode mode); + sigc::signal<void, nux::BaseTexture*> texture_updated; protected: @@ -83,6 +90,7 @@ private: bool _loading; float _opacity; int _handle; + DrawMode _draw_mode; }; } diff --git a/unity-shared/PreviewStyle.cpp b/unity-shared/PreviewStyle.cpp index 5947166fe..8dd27c8df 100644 --- a/unity-shared/PreviewStyle.cpp +++ b/unity-shared/PreviewStyle.cpp @@ -62,10 +62,10 @@ class Style::Impl public: Impl(Style* owner) : owner_(owner) - , preview_nav_left_texture_("/prev.svg") - , preview_nav_right_texture_("/next.svg") - , preview_play_texture_("/play.svg") - , preview_pause_texture_("/pause.svg") + , preview_nav_left_texture_("/preview_previous.svg", 32) + , preview_nav_right_texture_("/preview_next.svg", 32) + , preview_play_texture_("/preview_play.svg", 32) + , preview_pause_texture_("/preview_pause.svg", 32) { } ~Impl() {} @@ -113,6 +113,11 @@ int Style::GetNavigatorWidth() const return 42; } +int Style::GetNavigatorIconSize() const +{ + return 24; +} + int Style::GetPreviewPreferredHeight() const { return 390; @@ -168,6 +173,11 @@ int Style::GetTrackHeight() const return 28; } +int Style::GetMusicDurationWidth() const +{ + return 40; +} + int Style::GetActionButtonHeight() const { return 36; @@ -198,14 +208,51 @@ int Style::GetDetailsPanelMinimumWidth() const return 300; } +int Style::GetInfoHintIconSizeWidth() const +{ + return 24; +} + +int Style::GetInfoHintNameWidth() const +{ + return 100; +} + +float Style::GetDescriptionLineSpacing() const +{ + return 2.0; +} + +int Style::GetDescriptionLineCount() const +{ + return 20; +} + +int Style::GetRatingWidgetHeight() const +{ + return 36; +} + +int Style::GetStatusIconSize() const +{ + return 12; +} + +float Style::GetVideoImageAspectRatio() const +{ + return float(540)/380; +} + std::string Style::title_font() const { return "Ubuntu 22"; } + std::string Style::subtitle_size_font() const { return "Ubuntu 12.5"; } + std::string Style::description_font() const { return "Ubuntu Light 10"; @@ -215,10 +262,12 @@ std::string Style::app_license_font() const { return "Ubuntu Light 10"; } + std::string Style::app_last_update_font() const { return "Ubuntu Light 10"; } + std::string Style::app_copywrite_font() const { return "Ubuntu Light 10"; @@ -228,10 +277,17 @@ std::string Style::info_hint_font() const { return "Ubuntu Light 10"; } + +std::string Style::info_hint_bold_font() const +{ + return "Ubuntu Bold 10"; +} + std::string Style::user_rating_font() const { return "Ubuntu Light 10"; } + std::string Style::no_preview_image_font() const { return "Ubuntu Light 16"; diff --git a/unity-shared/PreviewStyle.h b/unity-shared/PreviewStyle.h index e81f5217c..a63a5b3de 100644 --- a/unity-shared/PreviewStyle.h +++ b/unity-shared/PreviewStyle.h @@ -57,6 +57,8 @@ public: static Style& Instance(); int GetNavigatorWidth() const; + int GetNavigatorIconSize() const; + int GetPreviewPreferredHeight() const; int GetPreviewPreferredWidth() const; @@ -74,6 +76,13 @@ public: int GetDetailsPanelMinimumWidth() const; + int GetInfoHintIconSizeWidth() const; + int GetInfoHintNameWidth() const; + + float GetDescriptionLineSpacing() const; + int GetDescriptionLineCount() const; + + int GetRatingWidgetHeight() const; std::string title_font() const; std::string subtitle_size_font() const; @@ -85,6 +94,7 @@ public: std::string app_last_update_font() const; std::string app_copywrite_font() const; std::string info_hint_font() const; + std::string info_hint_bold_font() const; std::string user_rating_font() const; std::string no_preview_image_font() const; @@ -103,7 +113,11 @@ public: //////////////////////////////// // Movie Preview + float GetVideoImageAspectRatio() const; + int GetTrackBarHeight() const; + int GetMusicDurationWidth() const; + int GetStatusIconSize() const; //////////////////////////////// nux::BaseTexture* GetNavLeftIcon(); diff --git a/unity-shared/RatingsButton.h b/unity-shared/RatingsButton.h index b162bc0a9..1d33c9ddd 100644 --- a/unity-shared/RatingsButton.h +++ b/unity-shared/RatingsButton.h @@ -16,7 +16,6 @@ * <http://www.gnu.org/licenses/> ** * Authored by: Gordon Allott <gord.allott@canonical.com> - * Nick Dedekind <nick.dedelomd@canonical.com> * */ @@ -28,6 +27,8 @@ #include <Nux/Nux.h> #include <Nux/ToggleButton.h> + + namespace unity { diff --git a/unity-shared/TextureThumbnailProvider.cpp b/unity-shared/TextureThumbnailProvider.cpp new file mode 100644 index 000000000..128daf8bf --- /dev/null +++ b/unity-shared/TextureThumbnailProvider.cpp @@ -0,0 +1,175 @@ +/* + * Copyright 2011 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 + * <http://www.gnu.org/licenses/> + ** + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#include <Nux/Nux.h> +#include "ThumbnailGenerator.h" +#include "UnityCore/GLibWrapper.h" +#include "TextureThumbnailProvider.h" + +namespace unity +{ + +namespace TextureThumbnailProvider +{ + +class GdkTextureThumbnailer : public Thumbnailer +{ +public: + GdkTextureThumbnailer(std::string const& name) + : name(name) + {} + + std::string name; + + virtual std::string GetName() const { return name; } + + virtual bool Run(int size, std::string const& input_file, std::string& output_file, std::string& error_hint); +}; + + +bool GdkTextureThumbnailer::Run(int size, std::string const& input_file, std::string& output_file, std::string& error_hint) +{ + GFileInputStream *stream; + GError *error = NULL; + GFile *file; + + /* try to open the source file for reading */ + file = g_file_new_for_uri (input_file.c_str()); + stream = g_file_read (file, NULL, &error); + g_object_unref (file); + + if (error != NULL) + { + error_hint = error->message; + g_error_free (error); + return false; + } + + glib::Object<GdkPixbuf> source_pixbuf(::gdk_pixbuf_new_from_stream_at_scale(G_INPUT_STREAM (stream), + -1, + size, + TRUE, + NULL, + NULL)); + + // glib::Object<GdkPixbuf> source_pixbuf(gdk_pixbuf_new_from_stream (G_INPUT_STREAM (stream), + // NULL, &error)); + g_object_unref (stream); + + if (error != NULL) + { + error_hint = error->message; + g_error_free (error); + return false; + } + + /************************** + * Generate Scaled buffer * + **************************/ + + gdouble hratio; + gdouble wratio; + gint dest_width = size; + gint dest_height = size; + gint source_width; + gint source_height; + + /* determine the source pixbuf dimensions */ + source_width = gdk_pixbuf_get_width (source_pixbuf); + source_height = gdk_pixbuf_get_height (source_pixbuf); + + /* return the same pixbuf if no scaling is required */ + if (source_width <= dest_width && source_height <= dest_height) + { + gdk_pixbuf_save(source_pixbuf, output_file.c_str(), "png", &error, NULL); + if (error != NULL) + { + error_hint = error->message; + g_error_free (error); + return false; + } + } + + /* determine which axis needs to be scaled down more */ + wratio = (gdouble) source_width / (gdouble) dest_width; + hratio = (gdouble) source_height / (gdouble) dest_height; + + /* adjust the other axis */ + if (hratio > wratio) + dest_width = rint (source_width / hratio); + else + dest_height = rint (source_height / wratio); + + + /* scale the pixbuf down to the desired size */ + glib::Object<GdkPixbuf> thumbnail_buffer(gdk_pixbuf_scale_simple (source_pixbuf, + MAX (dest_width, 1), MAX (dest_height, 1), + GDK_INTERP_BILINEAR)); + + gdk_pixbuf_save(thumbnail_buffer, output_file.c_str(), "png", &error, NULL); + if (error != NULL) + { + error_hint = error->message; + g_error_free (error); + return false; + } + return true; +} + +void Initialise() +{ + GSList *formats; + GSList *fp; + GStrv format_types; + guint n; + std::list<std::string> mime_types; + + /* get a list of all formats supported by GdkPixbuf */ + formats = gdk_pixbuf_get_formats (); + + /* iterate over all formats */ + for (fp = formats; fp != NULL; fp = fp->next) + { + /* ignore the disabled ones */ + if (!gdk_pixbuf_format_is_disabled ((GdkPixbufFormat*)fp->data)) + { + /* get a list of MIME types supported by this format */ + format_types = gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat*)fp->data); + + /* put them all in the unqiue MIME type hash table */ + for (n = 0; format_types != NULL && format_types[n] != NULL; ++n) + { + mime_types.push_back(format_types[n]); + } + + /* free the string array */ + g_strfreev (format_types); + } + } + + + Thumbnailer::Ptr thumbnailer(new GdkTextureThumbnailer("gdk_pixelbuffer")); + ThumbnailGenerator::RegisterThumbnailer(mime_types, thumbnailer); +} + +} // namespace TextureThumbnailProvider +} // namespace unity + diff --git a/unity-shared/TextureThumbnailProvider.h b/unity-shared/TextureThumbnailProvider.h new file mode 100644 index 000000000..3d16fddc0 --- /dev/null +++ b/unity-shared/TextureThumbnailProvider.h @@ -0,0 +1,38 @@ +/* + * Copyright 2011 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 + * <http://www.gnu.org/licenses/> + ** + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#ifndef UNITYSHARED_TEXTURETHUMBNAILPROVIDER_H +#define UNITYSHARED_TEXTURETHUMBNAILPROVIDER_H + +#include <Nux/Nux.h> + +namespace unity +{ + +namespace TextureThumbnailProvider +{ + void Initialise(); +}; + +} // namespace unity + +#endif // UNITYSHARED_TEXTURETHUMBNAILPROVIDER_H + diff --git a/unity-shared/ThumbnailGenerator.cpp b/unity-shared/ThumbnailGenerator.cpp new file mode 100644 index 000000000..e6d03ec14 --- /dev/null +++ b/unity-shared/ThumbnailGenerator.cpp @@ -0,0 +1,533 @@ +/* + * Copyright 2011 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 + * <http://www.gnu.org/licenses/> + ** + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#include <pthread.h> +#include <NuxCore/Logger.h> +#include "UnityCore/GLibSource.h" +#include "UnityCore/DesktopUtilities.h" +#include "ThumbnailGenerator.h" +#include <glib/gstdio.h> +#include <unordered_map> + +#include "TextureThumbnailProvider.h" +#include "DefaultThumbnailProvider.h" +#include "UserThumbnailProvider.h" +#include "config.h" + +namespace unity +{ + +namespace +{ + nux::logging::Logger logger("unity.thumbnailgenerator"); + ThumbnailGenerator* thumbnail_instance = nullptr; + + const unsigned int CLEANUP_DURATION = 60*1000; // 1 minute + const unsigned int CLEANUP_PREVIEW_AGE = 6*60*60*1000; // 6 hours + + static std::multimap<std::string, std::string> thumbnail_content_map; + static std::map<std::string, Thumbnailer::Ptr> thumbnailers_; + + pthread_mutex_t thumbnailers_mutex_ = PTHREAD_MUTEX_INITIALIZER; + + static std::string get_preview_dir() + { + return DesktopUtilities::GetUserDataDirectory().append("/previews"); + } +} + + +class Thumbnail +{ +public: + typedef std::shared_ptr<Thumbnail> Ptr; + + Thumbnail(std::string const& uri, unsigned int size, ThumbnailNotifier::Ptr notifier); + + virtual ~Thumbnail(); + + std::string Generate(std::string& error_hint); + + std::string const uri_; + unsigned int size_; + ThumbnailNotifier::Ptr notifier_; +}; + +NUX_IMPLEMENT_OBJECT_TYPE(ThumbnailNotifier); + +ThumbnailNotifier::ThumbnailNotifier() +: cancel_(g_cancellable_new()) +{ + +} + +void ThumbnailNotifier::Cancel() +{ + g_cancellable_cancel(cancel_); +} + +bool ThumbnailNotifier::IsCancelled() const +{ + return g_cancellable_is_cancelled(cancel_); +} + +class ThumbnailGeneratorImpl +{ +public: + ThumbnailGeneratorImpl(ThumbnailGenerator* parent) + : parent_(parent) + , thumbnails_mutex_(PTHREAD_MUTEX_INITIALIZER) + , thumbnail_thread_is_running_(false) + {} + + ~ThumbnailGeneratorImpl() + { + pthread_join(thumbnail_thread_, NULL); + } + + ThumbnailNotifier::Ptr GetThumbnail(std::string const& uri, int size); + void DoCleanup(); + + bool OnThumbnailComplete(); + + static std::list<Thumbnailer::Ptr> GetThumbnailers(std::string const& content_type, std::string& error_hint); + + void RunGenerate(); + void RunManagement(); + +private: + void StartCleanupTimer(); + +private: + ThumbnailGenerator* parent_; + + glib::Source::UniquePtr idle_; + glib::Source::UniquePtr idle_return_; + + /* Our mutex used when accessing data shared between the main thread and the + thumbnail thread, i.e. the thumbnail_thread_is_running flag and the + thumbnails_to_make list. */ + pthread_mutex_t thumbnails_mutex_; + + /* A flag to indicate whether a thumbnail thread is running, so we don't + start more than one. Lock thumbnails_mutex when accessing this. */ + volatile bool thumbnail_thread_is_running_; + pthread_t thumbnail_thread_; + + volatile bool management_thread_is_running_; + pthread_t management_thread_; + + glib::Source::UniquePtr cleanup_timer_; + + std::queue<Thumbnail::Ptr> thumbnails_; + + struct CompleteThumbnail + { + std::string thubnail_uri; + std::string error_hint; + ThumbnailNotifier::Ptr notifier; + }; + std::list<CompleteThumbnail> complete_thumbnails_; +}; + +static void* thumbnail_thread_start (void* data) +{ + ((ThumbnailGeneratorImpl*)data)->RunGenerate(); + return NULL; +} + +bool CheckCache(std::string const& uri_in, std::string& filename_out) +{ + // Check Cache. + std::hash<std::string> hash_fn; + std::stringstream ss_chache_thum; + ss_chache_thum << get_preview_dir() << "/" << hash_fn(uri_in) << ".png"; + filename_out = ss_chache_thum.str(); + + glib::Object<GFile> cache_file(g_file_new_for_path(filename_out.c_str())); + return g_file_query_exists(cache_file, NULL); +} + +ThumbnailNotifier::Ptr ThumbnailGeneratorImpl::GetThumbnail(std::string const& uri, int size) +{ + std::string cache_filename; + if (CheckCache(uri, cache_filename)) + { + pthread_mutex_lock (&thumbnails_mutex_); + + CompleteThumbnail complete_thumb; + complete_thumb.thubnail_uri = cache_filename; + complete_thumb.notifier = new ThumbnailNotifier(); + complete_thumbnails_.push_back(complete_thumb); + + // Delay the thumbnail update until after this method has returned with the notifier + if (!idle_return_) + { + idle_return_.reset(new glib::Idle(sigc::mem_fun(this, &ThumbnailGeneratorImpl::OnThumbnailComplete), glib::Source::Priority::LOW)); + } + + pthread_mutex_unlock (&thumbnails_mutex_); + + StartCleanupTimer(); + + return complete_thumb.notifier; + } + + pthread_mutex_lock (&thumbnails_mutex_); + /********************************* + * MUTEX LOCKED + *********************************/ + + if (!idle_ && thumbnail_thread_is_running_ == false) + { + idle_.reset(new glib::Idle([&]() + { + thumbnail_thread_is_running_ = true; + pthread_create (&thumbnail_thread_, NULL, thumbnail_thread_start, this); + idle_.reset(); + return false; + }, glib::Source::Priority::LOW)); + } + + ThumbnailNotifier::Ptr notifier(new ThumbnailNotifier()); + Thumbnail::Ptr thumb(new Thumbnail(uri, size, notifier)); + thumbnails_.push(thumb); + + pthread_mutex_unlock (&thumbnails_mutex_); + /********************************* + * MUTEX UNLOCKED + *********************************/ + + StartCleanupTimer(); + + + return notifier; +} + +void ThumbnailGeneratorImpl::StartCleanupTimer() +{ + if (!cleanup_timer_) + cleanup_timer_.reset(new glib::Timeout(CLEANUP_DURATION, [&]() { DoCleanup(); return false; })); +} + + +void ThumbnailGeneratorImpl::RunGenerate() +{ + for (;;) + { + /********************************* + * MUTEX LOCKED + *********************************/ + pthread_mutex_lock (&thumbnails_mutex_); + + if (thumbnails_.size() == 0) + { + thumbnail_thread_is_running_ = FALSE; + pthread_mutex_unlock (&thumbnails_mutex_); + pthread_exit (NULL); + } + + Thumbnail::Ptr thumb(thumbnails_.front()); + thumbnails_.pop(); + + pthread_mutex_unlock (&thumbnails_mutex_); + /********************************* + * MUTEX UNLOCKED + *********************************/ + + if (thumb->notifier_->IsCancelled()) + continue; + + std::string error_hint; + std::string uri_result = thumb->Generate(error_hint); + + /********************************* + * MUTEX LOCKED + *********************************/ + pthread_mutex_lock (&thumbnails_mutex_); + + CompleteThumbnail complete_thumb; + complete_thumb.thubnail_uri = uri_result; + complete_thumb.error_hint = error_hint; + complete_thumb.notifier = thumb->notifier_; + + complete_thumbnails_.push_back(complete_thumb); + + if (!idle_return_) + { + idle_return_.reset(new glib::Idle(sigc::mem_fun(this, &ThumbnailGeneratorImpl::OnThumbnailComplete), glib::Source::Priority::LOW)); + } + + pthread_mutex_unlock (&thumbnails_mutex_); + /********************************* + * MUTEX UNLOCKED + *********************************/ + } +} + +bool ThumbnailGeneratorImpl::OnThumbnailComplete() +{ + for (;;) + { + pthread_mutex_lock (&thumbnails_mutex_); + + if (complete_thumbnails_.size() == 0) + { + idle_return_.reset(); + pthread_mutex_unlock (&thumbnails_mutex_); + return false; + } + CompleteThumbnail complete_thumbnail = complete_thumbnails_.front(); + complete_thumbnails_.pop_front(); + + pthread_mutex_unlock (&thumbnails_mutex_); + + if (complete_thumbnail.notifier->IsCancelled()) + continue; + + if (complete_thumbnail.error_hint == "") + complete_thumbnail.notifier->ready.emit(complete_thumbnail.thubnail_uri); + else + complete_thumbnail.notifier->error.emit(complete_thumbnail.error_hint); + } + return false; +} + +std::list<Thumbnailer::Ptr> ThumbnailGeneratorImpl::GetThumbnailers(std::string const& content_type, std::string& error_hint) +{ + std::list<Thumbnailer::Ptr> thumbnailer_list; + + gchar** content_split = g_strsplit(content_type.c_str(), "/", -1); + + std::vector<std::string> content_list; + int i = 0; + while(content_split[i] != NULL) + { + if (g_strcmp0(content_split[i], "") != 0) + content_list.push_back(content_split[i]); + i++; + } + + unsigned int content_last = content_list.size(); + for (unsigned int i = 0; i < content_list.size()+1; ++i) + { + std::stringstream ss_content_type; + for (unsigned int j = 0; j < content_last; ++j) + { + ss_content_type << content_list[j]; + if (j < content_last-1) + ss_content_type << "/"; + } + + if (content_last == 0) + ss_content_type << "*"; + else if (content_last < content_list.size()) + ss_content_type << "/*"; + + content_last--; + + /********************************* + * FIND THUMBNAILER + *********************************/ + pthread_mutex_lock (&thumbnailers_mutex_); + + // have already got this content type? + auto range = thumbnail_content_map.equal_range(ss_content_type.str()); + + for (; range.first != range.second; range.first++) + { + // find the thumbnailer. + auto iter_tumbnailers = thumbnailers_.find(range.first->second); + if (iter_tumbnailers != thumbnailers_.end()) + { + thumbnailer_list.push_back(iter_tumbnailers->second); + } + } + pthread_mutex_unlock (&thumbnailers_mutex_); + } + + return thumbnailer_list; +} + +static void* management_thread_start (void* data) +{ + ((ThumbnailGeneratorImpl*)data)->RunManagement(); + return NULL; +} + +void ThumbnailGeneratorImpl::DoCleanup() +{ + cleanup_timer_.reset(); + + if (!management_thread_is_running_) + { + management_thread_is_running_ = true; + pthread_create (&management_thread_, NULL, management_thread_start, this); + } +} + +void ThumbnailGeneratorImpl::RunManagement() +{ + guint64 time = std::time(NULL) - CLEANUP_PREVIEW_AGE; + std::string thumbnail_folder_name = get_preview_dir(); + + GError* err = NULL; + GDir* thumbnailer_dir = g_dir_open(thumbnail_folder_name.c_str(), 0, &err); + if (err != NULL) + return; + + const gchar* file_basename = NULL;; + while ((file_basename = g_dir_read_name(thumbnailer_dir)) != NULL) + { + std::string filename = g_build_filename (thumbnail_folder_name.c_str(), file_basename, NULL); + + glib::Object<GFile> file(g_file_new_for_path(filename.c_str())); + + GError *err = NULL; + glib::Object<GFileInfo> file_info(g_file_query_info(file, G_FILE_ATTRIBUTE_TIME_CREATED, G_FILE_QUERY_INFO_NONE, NULL, &err)); + if (err != NULL) + { + g_error_free (err); + return; + } + + guint64 mtime = g_file_info_get_attribute_uint64(file_info, G_FILE_ATTRIBUTE_TIME_CREATED); + + if (mtime < time) + { + g_unlink(filename.c_str()); + } + } + + thumbnail_thread_is_running_ = false; +} + +ThumbnailGenerator::ThumbnailGenerator() +: pimpl(new ThumbnailGeneratorImpl(this)) +{ + if (thumbnail_instance) + { + LOG_ERROR(logger) << "More than one thumbnail generator created."; + } + else + { + thumbnail_instance = this; + + UserThumbnailProvider::Initialise(); + TextureThumbnailProvider::Initialise(); + DefaultThumbnailProvider::Initialise(); + } + +} + +ThumbnailGenerator::~ThumbnailGenerator() +{ + +} + +ThumbnailGenerator& ThumbnailGenerator::Instance() +{ + if (!thumbnail_instance) + { + LOG_ERROR(logger) << "No panel::Style created yet."; + } + + return *thumbnail_instance; +} + +ThumbnailNotifier::Ptr ThumbnailGenerator::GetThumbnail(std::string const& uri, int size) +{ + return pimpl->GetThumbnail(uri, size); +} + +void ThumbnailGenerator::RegisterThumbnailer(std::list<std::string> mime_types, Thumbnailer::Ptr thumbnailer) +{ + pthread_mutex_lock (&thumbnailers_mutex_); + + thumbnailers_[thumbnailer->GetName()] = thumbnailer; + + for (std::string mime_type : mime_types) + { + thumbnail_content_map.insert(std::pair<std::string,std::string>(mime_type, thumbnailer->GetName())); + } + + pthread_mutex_unlock (&thumbnailers_mutex_); +} + +void ThumbnailGenerator::DoCleanup() +{ + pimpl->DoCleanup(); +} + +Thumbnail::Thumbnail(std::string const& uri, unsigned int size, ThumbnailNotifier::Ptr notifier) +: uri_(uri) +, size_(size) +, notifier_(notifier) +{ +} + +Thumbnail::~Thumbnail() +{ +} + +std::string Thumbnail::Generate(std::string& error_hint) +{ + glib::Object<GFile> file(::g_file_new_for_uri(uri_.c_str())); + + GError *err = NULL; + glib::Object<GFileInfo> file_info(g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, NULL, &err)); + if (err != NULL) + { + error_hint = err->message; + g_error_free (err); + return ""; + } + + mkdir(get_preview_dir().c_str(), S_IRWXU); + + std::string file_type = g_file_info_get_attribute_string(file_info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE); + + std::list<Thumbnailer::Ptr> thumbnailers = ThumbnailGeneratorImpl::GetThumbnailers(file_type, error_hint); + + std::hash<std::string> hash_fn; + std::stringstream ss_output; + ss_output << get_preview_dir() << "/"; + ss_output << hash_fn(uri_) << ".png"; + + std::string output_file; + for (Thumbnailer::Ptr const& thumbnailer : thumbnailers) + { + output_file = ss_output.str(); + + if (thumbnailer->Run(size_, uri_, output_file, error_hint)) + { + error_hint.clear(); + return output_file; + } + } + if (error_hint == "") + error_hint = "Could not find thumbnailer"; + return ""; +} + + + +} // namespace unity \ No newline at end of file diff --git a/unity-shared/ThumbnailGenerator.h b/unity-shared/ThumbnailGenerator.h new file mode 100644 index 000000000..f786f3d22 --- /dev/null +++ b/unity-shared/ThumbnailGenerator.h @@ -0,0 +1,85 @@ +/* + * Copyright 2011 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 + * <http://www.gnu.org/licenses/> + ** + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#ifndef UNITYSHARED_THUMBNAILGENERATOR_H +#define UNITYSHARED_THUMBNAILGENERATOR_H + +#include <Nux/Nux.h> +#include "UnityCore/GLibWrapper.h" + +namespace unity +{ + +class Thumbnailer +{ +public: + typedef std::shared_ptr<Thumbnailer> Ptr; + + virtual std::string GetName() const = 0; + + virtual bool Run(int size, std::string const& input_file, std::string& output_file, std::string& error_hint) = 0; +}; + +class ThumbnailNotifier : public nux::Object +{ +public: + typedef nux::ObjectPtr<ThumbnailNotifier> Ptr; + NUX_DECLARE_OBJECT_TYPE(ThumbnailNotifier, Object); + + ThumbnailNotifier(); + + void Cancel(); + bool IsCancelled() const; + + sigc::signal<void, std::string> ready; + sigc::signal<void, std::string> error; + +private: + glib::Object<GCancellable> cancel_; +}; + + +class ThumbnailGeneratorImpl; + +class ThumbnailGenerator +{ +public: + ThumbnailGenerator(); + virtual ~ThumbnailGenerator(); + + static ThumbnailGenerator& Instance(); + + static void RegisterThumbnailer(std::list<std::string> mime_types, Thumbnailer::Ptr thumbnailer); + + ThumbnailNotifier::Ptr GetThumbnail(std::string const& uri, int size); + + void DoCleanup(); + +protected: + std::unique_ptr<ThumbnailGeneratorImpl> pimpl; + + friend class Thumbnail; +}; + +} // namespace unity + +#endif // UNITYSHARED_THUMBNAILGENERATOR_H + diff --git a/unity-shared/UserThumbnailProvider.cpp b/unity-shared/UserThumbnailProvider.cpp new file mode 100644 index 000000000..2b8511480 --- /dev/null +++ b/unity-shared/UserThumbnailProvider.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2011 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 + * <http://www.gnu.org/licenses/> + ** + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#include <Nux/Nux.h> +#include "UnityCore/GLibWrapper.h" +#include "UserThumbnailProvider.h" +#include "ThumbnailGenerator.h" + +namespace unity +{ + +namespace UserThumbnailProvider +{ + +class UserThumbnailer : public Thumbnailer +{ +public: + UserThumbnailer(std::string const& name, std::string const& command_line) + : name(name) + , command_line(command_line) + {} + + std::string name; + std::string command_line; + + virtual std::string GetName() const { return name; } + + virtual bool Run(int size, std::string const& input_file, std::string& output_file, std::string& error_hint); +}; + +bool UserThumbnailer::Run(int size, std::string const& input_file, std::string& output_file, std::string& error_hint) +{ + std::string tmp_command_line = command_line; + + // replace size + size_t pos = tmp_command_line.find("%s"); + std::stringstream ss; ss << size; + if (pos != std::string::npos) { tmp_command_line.replace(pos, 2, ss.str()); } + + // replace input file name + pos = tmp_command_line.find("%u"); + if (pos != std::string::npos) { tmp_command_line.replace(pos, 2, input_file); } + + // replace output file name + pos = tmp_command_line.find("%o"); + if (pos != std::string::npos) { tmp_command_line.replace(pos, 2, output_file); } + + gint exit_status = 0; + GError* err = NULL; + g_spawn_command_line_sync(tmp_command_line.c_str(), NULL, NULL, &exit_status, &err); + if (err != NULL) + { + error_hint = err->message; + g_error_free (err); + return false; + } + else if (exit_status != 0) + { + std::stringstream ss; + ss << "Failed to create thumbnail. Program exited with exit_status=" << exit_status; + error_hint = ss.str(); + return false; + } + + return true; +} + +void Initialise() +{ + GError* err = NULL; + GDir* thumbnailer_dir = g_dir_open("/usr/share/thumbnailers", 0, &err); + if (err != NULL) + return; + + const gchar* file; + while((file = g_dir_read_name(thumbnailer_dir)) != NULL) + { + std::string file_name(file); + if (file_name == "." || file_name == "..") + continue; + + /********************************* + * READ SETTINGS + *********************************/ + + GKeyFile* key_file = g_key_file_new(); + + err=NULL; + if (!g_key_file_load_from_file (key_file, (std::string("/usr/share/thumbnailers/") + file_name).c_str(), G_KEY_FILE_NONE, &err)) + { + g_key_file_free(key_file); + g_error_free(err); + continue; + } + + err=NULL; + glib::String command_line(g_key_file_get_string (key_file, "Thumbnailer Entry", "Exec", &err)); + if (err != NULL) + { + g_key_file_free(key_file); + g_error_free(err); + continue; + } + + err=NULL; + gsize mime_count = 0; + gchar** mime_types = g_key_file_get_string_list (key_file, "Thumbnailer Entry", "MimeType", &mime_count, &err); + if (err != NULL) + { + g_key_file_free(key_file); + g_error_free(err); + continue; + } + + Thumbnailer::Ptr thumbnailer(new UserThumbnailer(file_name, command_line.Value())); + std::list<std::string> mime_type_list; + for (gsize i = 0; i < mime_count && mime_types[i] != NULL; i++) + { + mime_type_list.push_front(mime_types[i]); + } + + ThumbnailGenerator::RegisterThumbnailer(mime_type_list, thumbnailer); + + g_strfreev(mime_types); + g_key_file_free(key_file); + } + + g_dir_close(thumbnailer_dir); +} + +} // namespace DefaultThumbnailProvider +} // namespace unity + + diff --git a/unity-shared/UserThumbnailProvider.h b/unity-shared/UserThumbnailProvider.h new file mode 100644 index 000000000..a3058307c --- /dev/null +++ b/unity-shared/UserThumbnailProvider.h @@ -0,0 +1,38 @@ +/* + * Copyright 2011 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 + * <http://www.gnu.org/licenses/> + ** + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#ifndef UNITYSHARED_USERTHUMBNAILPROVIDER_H +#define UNITYSHARED_USERTHUMBNAILPROVIDER_H + +#include <Nux/Nux.h> + +namespace unity +{ + +namespace UserThumbnailProvider +{ + void Initialise(); +}; + +} // namespace unity + +#endif // UNITYSHARED_USERTHUMBNAILPROVIDER_H + |
