Back to index

unity  6.0.0
PlacesGroup.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2011 Canonical Ltd.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of the GNU Lesser General Public License version 3, as
00006  * published by the  Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranties of
00010  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00011  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00012  * License for more details.
00013  *
00014  * You should have received a copy of both the GNU Lesser General Public
00015  * License version 3 along with this program.  If not, see
00016  * <http://www.gnu.org/licenses/>
00017  *
00018  * Authored by: Gordon Allott <gord.allott@canonical.com>
00019  *
00020  */
00021 
00022 #include <Nux/Nux.h>
00023 #include <NuxCore/Logger.h>
00024 #include <NuxCore/Math/MathInc.h>
00025 
00026 #include "PlacesGroup.h"
00027 #include <glib.h>
00028 #include <glib/gi18n-lib.h>
00029 
00030 #include <UnityCore/Variant.h>
00031 #include <UnityCore/GLibWrapper.h>
00032 
00033 #include "unity-shared/StaticCairoText.h"
00034 #include "unity-shared/DashStyle.h"
00035 #include "unity-shared/LineSeparator.h"
00036 #include "unity-shared/ubus-server.h"
00037 #include "unity-shared/UBusMessages.h"
00038 
00039 
00040 namespace
00041 {
00042 nux::logging::Logger logger("unity.dash.placesgroup");
00043 }
00044 
00045 namespace unity
00046 {
00047 namespace
00048 {
00049 
00050 const nux::Color kExpandDefaultTextColor(1.0f, 1.0f, 1.0f, 0.5f);
00051 const float kExpandDefaultIconOpacity = 0.5f;
00052 
00053 // Category  highlight
00054 const int kHighlightHeight = 24;
00055 const int kHighlightRightPadding = 10 - 3; // -3 because the scrollbar is not a real overlay scrollbar!
00056 const int kHighlightLeftPadding = 10;
00057 
00058 // Font
00059 const char* const NAME_LABEL_FONT = "Ubuntu 13"; // 17px = 13
00060 const char* const EXPANDER_LABEL_FONT = "Ubuntu 10"; // 13px = 10
00061 
00062 class HeaderView : public nux::View
00063 {
00064 public:
00065   HeaderView(NUX_FILE_LINE_DECL)
00066    : nux::View(NUX_FILE_LINE_PARAM)
00067   {
00068     SetAcceptKeyNavFocusOnMouseDown(false);
00069     SetAcceptKeyNavFocusOnMouseEnter(true);
00070   }
00071 
00072 protected:
00073   void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw)
00074   {
00075   };
00076 
00077   void DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw)
00078   {
00079     if (IsFullRedraw() && GetLayout())
00080     {
00081       nux::GetPainter().PushPaintLayerStack();
00082       GetLayout()->ProcessDraw(graphics_engine, force_draw);
00083       nux::GetPainter().PopPaintLayerStack();
00084     }
00085   }
00086 
00087   bool AcceptKeyNavFocus()
00088   {
00089     return true;
00090   }
00091 
00092   nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type)
00093   {
00094     if (event_type != nux::EVENT_MOUSE_WHEEL &&
00095         TestMousePointerInclusion(mouse_position, event_type))
00096       return this;
00097     else
00098       return nullptr;
00099   }
00100 };
00101 
00102 }
00103 
00104 NUX_IMPLEMENT_OBJECT_TYPE(PlacesGroup);
00105 
00106 PlacesGroup::PlacesGroup()
00107   : AbstractPlacesGroup(),
00108     _child_view(nullptr),
00109     _is_expanded(true),
00110     _n_visible_items_in_unexpand_mode(0),
00111     _n_total_items(0)
00112 {
00113   dash::Style& style = dash::Style::Instance();
00114 
00115   SetAcceptKeyNavFocusOnMouseDown(false);
00116   SetAcceptKeyNavFocusOnMouseEnter(false);
00117 
00118   nux::BaseTexture* arrow = style.GetGroupUnexpandIcon();
00119 
00120   _group_layout = new nux::VLayout("", NUX_TRACKER_LOCATION);
00121 
00122   // -2 because the icons have an useless border.
00123   int top_space = style.GetPlacesGroupTopSpace() - 2;
00124   _group_layout->AddLayout(new nux::SpaceLayout(top_space, top_space, top_space, top_space), 0);
00125 
00126   _header_view = new HeaderView(NUX_TRACKER_LOCATION);
00127   _group_layout->AddView(_header_view, 0, nux::MINOR_POSITION_TOP, nux::MINOR_SIZE_FULL);
00128 
00129   _header_layout = new nux::HLayout(NUX_TRACKER_LOCATION);
00130   _header_layout->SetLeftAndRightPadding(style.GetCategoryHeaderLeftPadding(), 0);
00131   _header_layout->SetSpaceBetweenChildren(10);
00132   _header_view->SetLayout(_header_layout);
00133 
00134   _icon = new IconTexture("", 24);
00135   _icon->SetMinMaxSize(24, 24);
00136   _header_layout->AddView(_icon, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX);
00137 
00138   _text_layout = new nux::HLayout(NUX_TRACKER_LOCATION);
00139   _text_layout->SetHorizontalInternalMargin(15);
00140   _header_layout->AddLayout(_text_layout, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT);
00141 
00142   _name = new nux::StaticCairoText("", NUX_TRACKER_LOCATION);
00143   _name->SetFont(NAME_LABEL_FONT);
00144   _name->SetTextEllipsize(nux::StaticCairoText::NUX_ELLIPSIZE_END);
00145   _name->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_LEFT);
00146   _text_layout->AddView(_name, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT);
00147 
00148   _expand_layout = new nux::HLayout(NUX_TRACKER_LOCATION);
00149   _expand_layout->SetHorizontalInternalMargin(8);
00150   _text_layout->AddLayout(_expand_layout, 0, nux::MINOR_POSITION_END, nux::MINOR_SIZE_MATCHCONTENT);
00151 
00152   _expand_label_layout = new nux::HLayout(NUX_TRACKER_LOCATION);
00153   _expand_layout->AddLayout(_expand_label_layout, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT);
00154 
00155   _expand_label = new nux::StaticCairoText("", NUX_TRACKER_LOCATION);
00156   _expand_label->SetFont(EXPANDER_LABEL_FONT);
00157   _expand_label->SetTextEllipsize(nux::StaticCairoText::NUX_ELLIPSIZE_END);
00158   _expand_label->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_LEFT);
00159   _expand_label->SetTextColor(kExpandDefaultTextColor);
00160   _expand_label_layout->AddView(_expand_label, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX);
00161 
00162   _expand_icon = new IconTexture(arrow, arrow->GetWidth(), arrow->GetHeight());
00163   _expand_icon->SetOpacity(kExpandDefaultIconOpacity);
00164   _expand_icon->SetMinimumSize(arrow->GetWidth(), arrow->GetHeight());
00165   _expand_icon->SetVisible(false);
00166   _expand_layout->AddView(_expand_icon, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX);
00167 
00168   separator_layout_ = new nux::HLayout();
00169   separator_layout_->SetLeftAndRightPadding(style.GetCategorySeparatorLeftPadding(),
00170                                             style.GetCategorySeparatorRightPadding() - style.GetScrollbarWidth());
00171 
00172   separator_ = new HSeparator;
00173   separator_layout_->AddView(separator_, 1);
00174 
00175   draw_separator.changed.connect(sigc::mem_fun(this, &PlacesGroup::DrawSeparatorChanged));
00176 
00177   SetLayout(_group_layout);
00178 
00179   // don't need to disconnect these signals as they are disconnected when this object destroys the contents
00180   _header_view->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
00181   _header_view->key_nav_focus_change.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelFocusChanged));
00182   _header_view->key_nav_focus_activate.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelActivated));
00183   _icon->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
00184   _name->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
00185   _expand_label->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
00186   _expand_icon->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
00187 
00188   key_nav_focus_change.connect([&](nux::Area* area, bool has_focus, nux::KeyNavDirection direction)
00189   {
00190     if (!has_focus)
00191       return;
00192 
00193     if(direction == nux::KEY_NAV_UP)
00194       nux::GetWindowCompositor().SetKeyFocusArea(_child_view, direction);
00195     else
00196       nux::GetWindowCompositor().SetKeyFocusArea(GetHeaderFocusableView(), direction);
00197   });
00198 }
00199 
00200 void PlacesGroup::DrawSeparatorChanged(bool draw)
00201 {
00202   if (draw and !separator_layout_->IsChildOf(_group_layout))
00203     _group_layout->AddView(separator_layout_.GetPointer(), 0);
00204   else if (!draw and separator_layout_->IsChildOf(_group_layout))
00205     _group_layout->RemoveChildObject(separator_layout_.GetPointer());
00206   QueueDraw();
00207 }
00208 
00209 void
00210 PlacesGroup::OnLabelActivated(nux::Area* label)
00211 {
00212   SetExpanded(!_is_expanded);
00213 }
00214 
00215 void
00216 PlacesGroup::OnLabelFocusChanged(nux::Area* label, bool has_focus, nux::KeyNavDirection direction)
00217 {
00218   if (HeaderHasKeyFocus())
00219   {
00220     _ubus.SendMessage(UBUS_RESULT_VIEW_KEYNAV_CHANGED,
00221                       g_variant_new("(iiii)", 0, 0, 0, 0));
00222   }
00223 
00224   QueueDraw();
00225 }
00226 
00227 void
00228 PlacesGroup::SetName(std::string const& name)
00229 {
00230   if (_cached_name != name)
00231   {
00232     _cached_name = name;
00233     _name->SetText(glib::String(g_markup_escape_text(name.c_str(), -1)).Str());
00234   }
00235 }
00236 
00237 nux::StaticCairoText*
00238 PlacesGroup::GetLabel()
00239 {
00240   return _name;
00241 }
00242 
00243 nux::StaticCairoText*
00244 PlacesGroup::GetExpandLabel()
00245 {
00246   return _expand_label;
00247 }
00248 
00249 void
00250 PlacesGroup::SetIcon(std::string const& path_to_emblem)
00251 {
00252   _icon->SetByIconName(path_to_emblem, 24);
00253 }
00254 
00255 void
00256 PlacesGroup::SetChildView(dash::ResultView* view)
00257 {
00258   debug::Introspectable *i = dynamic_cast<debug::Introspectable*>(view);
00259   if (i)
00260     AddChild(i);
00261   _child_view = view;
00262 
00263   nux::VLayout* layout = new nux::VLayout();
00264   layout->AddView(_child_view, 0);
00265 
00266   layout->SetLeftAndRightPadding(25, 0);
00267   _group_layout->AddLayout(new nux::SpaceLayout(8,8,8,8), 0); // top padding
00268   _group_layout->AddLayout(layout, 1);
00269 
00270   view->results_per_row.changed.connect([&] (int results_per_row) 
00271   {
00272     _n_visible_items_in_unexpand_mode = results_per_row;  
00273     RefreshLabel();
00274   });
00275 
00276   QueueDraw();
00277 }
00278 
00279 nux::View*
00280 PlacesGroup::GetChildView()
00281 {
00282   return _child_view;
00283 }
00284 
00285 void PlacesGroup::SetChildLayout(nux::Layout* layout)
00286 {
00287   _group_layout->AddLayout(layout, 1);
00288   QueueDraw();
00289 }
00290 
00291 void
00292 PlacesGroup::RefreshLabel()
00293 {
00294   std::string result_string;
00295 
00296   if (_n_visible_items_in_unexpand_mode < _n_total_items)
00297   {
00298     if (_is_expanded)
00299     {
00300       result_string = _("See fewer results");
00301     }
00302     else
00303     {
00304       LOG_DEBUG(logger) << _n_total_items << " - " << _n_visible_items_in_unexpand_mode;
00305       result_string = glib::String(g_strdup_printf(g_dngettext(GETTEXT_PACKAGE,
00306                                                   "See one more result",
00307                                                   "See %d more results",
00308                                                   _n_total_items - _n_visible_items_in_unexpand_mode),
00309                                       _n_total_items - _n_visible_items_in_unexpand_mode)).Str();
00310     }
00311   }
00312 
00313   _expand_icon->SetVisible(!(_n_visible_items_in_unexpand_mode >= _n_total_items && _n_total_items != 0));
00314   SetName(_cached_name);
00315 
00316   _expand_label->SetText(result_string);
00317   _expand_label->SetVisible(_n_visible_items_in_unexpand_mode < _n_total_items);
00318 
00319   // See bug #748101 ("Dash - "See more..." line should be base-aligned with section header")
00320   // We're making two assumptions here:
00321   // [a] The font size _name is bigger than the font size of _expand_label
00322   // [b] The bottom sides have the same y coordinate
00323   int bottom_padding = _name->GetBaseHeight() - _name->GetBaseline() -
00324                        (_expand_label->GetBaseHeight() - _expand_label->GetBaseline());
00325 
00326   _expand_label_layout->SetTopAndBottomPadding(0, bottom_padding);
00327 
00328   QueueDraw();
00329 }
00330 
00331 void
00332 PlacesGroup::Refresh()
00333 {
00334   RefreshLabel();
00335   ComputeContentSize();
00336   QueueDraw();
00337 }
00338 
00339 
00340 void
00341 PlacesGroup::Relayout()
00342 {
00343   if (_relayout_idle)
00344     return;
00345 
00346   _relayout_idle.reset(new glib::Idle(glib::Source::Priority::HIGH));
00347   _relayout_idle->Run(sigc::mem_fun(this, &PlacesGroup::OnIdleRelayout));
00348 }
00349 
00350 bool
00351 PlacesGroup::OnIdleRelayout()
00352 {
00353   if (GetChildView())
00354   {
00355     Refresh();
00356     QueueDraw();
00357     _group_layout->QueueDraw();
00358     GetChildView()->QueueDraw();
00359     ComputeContentSize();
00360     _relayout_idle.reset();
00361   }
00362 
00363   return false;
00364 }
00365 
00366 long PlacesGroup::ComputeContentSize()
00367 {
00368   long ret = nux::View::ComputeContentSize();
00369 
00370   nux::Geometry const& geo = GetGeometry();
00371 
00372   if (_cached_geometry != geo)
00373   {
00374     _focus_layer.reset(dash::Style::Instance().FocusOverlay(geo.width - kHighlightLeftPadding - kHighlightRightPadding, kHighlightHeight));
00375 
00376     _cached_geometry = geo;
00377   }
00378 
00379   return ret;
00380 }
00381 
00382 void PlacesGroup::Draw(nux::GraphicsEngine& graphics_engine,
00383                        bool                 forceDraw)
00384 {
00385   nux::Geometry const& base = GetGeometry();
00386   graphics_engine.PushClippingRectangle(base);
00387 
00388   if (ShouldBeHighlighted())
00389   {
00390     nux::Geometry geo(_header_layout->GetGeometry());
00391     geo.width = base.width - kHighlightRightPadding - kHighlightLeftPadding;
00392     geo.x += kHighlightLeftPadding;
00393 
00394     _focus_layer->SetGeometry(geo);
00395     _focus_layer->Renderlayer(graphics_engine);
00396   }
00397 
00398   graphics_engine.PopClippingRectangle();
00399 }
00400 
00401 void
00402 PlacesGroup::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw)
00403 {
00404   nux::Geometry const& base = GetGeometry();
00405 
00406   graphics_engine.PushClippingRectangle(base);
00407 
00408   if (ShouldBeHighlighted() && !IsFullRedraw() && _focus_layer)
00409   {
00410     nux::GetPainter().PushLayer(graphics_engine, _focus_layer->GetGeometry(), _focus_layer.get());
00411   }
00412 
00413   _group_layout->ProcessDraw(graphics_engine, force_draw);
00414 
00415   graphics_engine.PopClippingRectangle();
00416 }
00417 
00418 void PlacesGroup::PostDraw(nux::GraphicsEngine& graphics_engine,
00419                            bool                 forceDraw)
00420 {
00421 }
00422 
00423 void
00424 PlacesGroup::SetCounts(guint n_visible_items_in_unexpand_mode, guint n_total_items)
00425 {
00426   _n_total_items = n_total_items;
00427 
00428   Relayout();
00429 }
00430 
00431 bool
00432 PlacesGroup::GetExpanded() const
00433 {
00434   return _is_expanded;
00435 }
00436 
00437 void
00438 PlacesGroup::SetExpanded(bool is_expanded)
00439 {
00440   if (_is_expanded == is_expanded)
00441     return;
00442 
00443   if (is_expanded && _n_total_items <= _n_visible_items_in_unexpand_mode)
00444     return;
00445 
00446   _is_expanded = is_expanded;
00447 
00448   Refresh();
00449 
00450   dash::Style& style = dash::Style::Instance();
00451   if (_is_expanded)
00452     _expand_icon->SetTexture(style.GetGroupUnexpandIcon());
00453   else
00454     _expand_icon->SetTexture(style.GetGroupExpandIcon());
00455 
00456   expanded.emit(this);
00457 }
00458 
00459 void
00460 PlacesGroup::RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags)
00461 {
00462   SetExpanded(!_is_expanded);
00463 }
00464 
00465 void
00466 PlacesGroup::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags)
00467 {
00468   QueueDraw();
00469 }
00470 
00471 void
00472 PlacesGroup::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags)
00473 {
00474   QueueDraw();
00475 }
00476 
00477 int
00478 PlacesGroup::GetHeaderHeight() const
00479 {
00480   return _header_layout->GetGeometry().height;
00481 }
00482 
00483 bool PlacesGroup::HeaderHasKeyFocus() const
00484 {
00485   return (_header_view && _header_view->HasKeyFocus());
00486 }
00487 
00488 bool PlacesGroup::HeaderIsFocusable() const
00489 {
00490   return (_header_view != nullptr);
00491 }
00492 
00493 nux::View* PlacesGroup::GetHeaderFocusableView() const
00494 {
00495   return _header_view;
00496 }
00497 
00498 bool PlacesGroup::ShouldBeHighlighted() const
00499 {
00500   return HeaderHasKeyFocus();
00501 }
00502 
00503 //
00504 // Key navigation
00505 //
00506 bool
00507 PlacesGroup::AcceptKeyNavFocus()
00508 {
00509   return true;
00510 }
00511 
00512 //
00513 // Introspection
00514 //
00515 std::string PlacesGroup::GetName() const
00516 {
00517   return "PlacesGroup";
00518 }
00519 
00520 void PlacesGroup::AddProperties(GVariantBuilder* builder)
00521 {
00522   unity::variant::BuilderWrapper wrapper(builder);
00523 
00524   wrapper.add("header-x", _header_view->GetAbsoluteX());
00525   wrapper.add("header-y", _header_view->GetAbsoluteY());
00526   wrapper.add("header-width", _header_view->GetAbsoluteWidth());
00527   wrapper.add("header-height", _header_view->GetAbsoluteHeight());
00528   wrapper.add("header-has-keyfocus", HeaderHasKeyFocus());
00529   wrapper.add("header-is-highlighted", ShouldBeHighlighted());
00530   wrapper.add("name", _name->GetText());
00531   wrapper.add("is-visible", IsVisible());
00532   wrapper.add("is-expanded", GetExpanded());
00533   wrapper.add("expand-label-is-visible", _expand_label->IsVisible());
00534   wrapper.add("expand-label-y", _expand_label->GetAbsoluteY());
00535   wrapper.add("expand-label-baseline", _expand_label->GetBaseline());
00536   wrapper.add("name-label-y", _name->GetAbsoluteY());
00537   wrapper.add("name-label-baseline", _name->GetBaseline());
00538 }
00539 
00540 } // namespace unity