Back to index

unity  6.0.0
SwitcherView.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2011 Canonical Ltd
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU 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,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authored by: Jason Smith <jason.smith@canonical.com>
00017  */
00018 
00019 #include "config.h"
00020 
00021 #include "SwitcherView.h"
00022 #include "unity-shared/IconRenderer.h"
00023 #include "LayoutSystem.h"
00024 
00025 #include "unity-shared/TimeUtil.h"
00026 
00027 #include <UnityCore/Variant.h>
00028 
00029 #include <NuxCore/Object.h>
00030 #include <Nux/Nux.h>
00031 #include <Nux/WindowCompositor.h>
00032 
00033 namespace unity
00034 {
00035 using namespace ui;
00036 using launcher::AbstractLauncherIcon;
00037 
00038 namespace switcher
00039 {
00040 
00041 NUX_IMPLEMENT_OBJECT_TYPE(SwitcherView);
00042 
00043 SwitcherView::SwitcherView()
00044   : UnityWindowView()
00045   , target_sizes_set_(false)
00046 {
00047   icon_renderer_ = AbstractIconRenderer::Ptr(new IconRenderer());
00048   icon_renderer_->pip_style = OVER_TILE;
00049 
00050   layout_system_ = LayoutSystem::Ptr (new LayoutSystem ());
00051   border_size = 50;
00052   flat_spacing = 10;
00053   icon_size = 128;
00054   minimum_spacing = 10;
00055   tile_size = 150;
00056   vertical_size = tile_size + 80;
00057   text_size = 15;
00058   animation_length = 250;
00059   monitor = -1;
00060   spread_size = 3.5f;
00061   render_boxes = false;
00062 
00063   animation_draw_ = false;
00064 
00065   save_time_.tv_sec = 0;
00066   save_time_.tv_nsec = 0;
00067 
00068   render_targets_.clear ();
00069 
00070   rounding_texture_.Adopt(nux::CreateTexture2DFromFile(PKGDATADIR"/switcher_round_rect.png", -1, true));
00071 
00072   text_view_ = new nux::StaticCairoText("Testing");
00073   text_view_->SetMaximumWidth ((int) (tile_size * spread_size));
00074   text_view_->SetLines(1);
00075   text_view_->SetTextColor(nux::color::White);
00076   text_view_->SetFont("Ubuntu Bold 10");
00077 
00078   icon_size.changed.connect (sigc::mem_fun (this, &SwitcherView::OnIconSizeChanged));
00079   tile_size.changed.connect (sigc::mem_fun (this, &SwitcherView::OnTileSizeChanged));
00080 }
00081 
00082 std::string SwitcherView::GetName() const
00083 {
00084   return "SwitcherView";
00085 }
00086 
00087 void SwitcherView::AddProperties(GVariantBuilder* builder)
00088 {
00089   unity::variant::BuilderWrapper(builder)
00090   .add("render-boxes", render_boxes)
00091   .add("border-size", border_size)
00092   .add("flat-spacing", flat_spacing)
00093   .add("icon-size", icon_size)
00094   .add("minimum-spacing", minimum_spacing)
00095   .add("tile-size", tile_size)
00096   .add("vertical-size", vertical_size)
00097   .add("text-size", text_size)
00098   .add("animation-length", animation_length)
00099   .add("spread-size", (float)spread_size)
00100   .add("label", text_view_->GetText());
00101 }
00102 
00103 
00104 
00105 LayoutWindowList SwitcherView::ExternalTargets ()
00106 {
00107   LayoutWindowList result = render_targets_;
00108   return result;
00109 }
00110 
00111 void SwitcherView::SetModel(SwitcherModel::Ptr model)
00112 {
00113   model_ = model;
00114   model->selection_changed.connect(sigc::mem_fun(this, &SwitcherView::OnSelectionChanged));
00115   model->detail_selection.changed.connect (sigc::mem_fun (this, &SwitcherView::OnDetailSelectionChanged));
00116   model->detail_selection_index.changed.connect (sigc::mem_fun (this, &SwitcherView::OnDetailSelectionIndexChanged));
00117 
00118   if (!model->Selection())
00119     return;
00120 
00121   if (model->detail_selection)
00122   {
00123     Window detail_window = model->DetailSelectionWindow();
00124     text_view_->SetText(model->Selection()->NameForWindow(detail_window));
00125   }
00126   else
00127   {
00128     text_view_->SetText(model->Selection()->tooltip_text());
00129   }
00130 }
00131 
00132 void SwitcherView::OnIconSizeChanged (int size)
00133 {
00134   icon_renderer_->SetTargetSize(tile_size, icon_size, 10);
00135 }
00136 
00137 void SwitcherView::OnTileSizeChanged (int size)
00138 {
00139   icon_renderer_->SetTargetSize(tile_size, icon_size, 10);
00140   vertical_size = tile_size + 80;
00141 }
00142 
00143 void SwitcherView::SaveLast ()
00144 {
00145   saved_args_ = last_args_;
00146   saved_background_ = last_background_;
00147   clock_gettime(CLOCK_MONOTONIC, &save_time_);
00148 }
00149 
00150 void SwitcherView::OnDetailSelectionIndexChanged (unsigned int index)
00151 {
00152   if (model_->detail_selection)
00153   {
00154     Window detail_window = model_->DetailSelectionWindow();
00155     text_view_->SetText(model_->Selection()->NameForWindow(detail_window));
00156   }
00157   QueueDraw ();
00158 }
00159 
00160 void SwitcherView::OnDetailSelectionChanged (bool detail)
00161 {
00162 
00163   if (detail)
00164   {
00165     Window detail_window = model_->DetailSelectionWindow();
00166     text_view_->SetText(model_->Selection()->NameForWindow(detail_window));
00167   }
00168   else
00169   {
00170     text_view_->SetText(model_->Selection()->tooltip_text());
00171   }
00172   SaveLast ();
00173   QueueDraw ();
00174 }
00175 
00176 void SwitcherView::OnSelectionChanged(AbstractLauncherIcon::Ptr selection)
00177 {
00178   if (selection)
00179     text_view_->SetText(selection->tooltip_text());
00180   SaveLast ();
00181   QueueDraw();
00182 }
00183 
00184 SwitcherModel::Ptr SwitcherView::GetModel()
00185 {
00186   return model_;
00187 }
00188 
00189 RenderArg SwitcherView::CreateBaseArgForIcon(AbstractLauncherIcon::Ptr icon)
00190 {
00191   RenderArg arg;
00192   arg.icon = icon.GetPointer();
00193   arg.alpha = 0.95f;
00194 
00195   // tells the renderer to render arrows by number
00196   arg.running_on_viewport = true;
00197 
00198   arg.window_indicators = icon->WindowsForMonitor(monitor).size();
00199   if (arg.window_indicators > 1)
00200     arg.running_arrow = true;
00201   else
00202     arg.window_indicators = 0;
00203 
00204   if (icon == model_->Selection())
00205   {
00206     arg.keyboard_nav_hl = true;
00207     arg.backlight_intensity = 1.0f;
00208   }
00209   else
00210   {
00211     arg.backlight_intensity = 0.7f;
00212   }
00213 
00214   return arg;
00215 }
00216 
00217 RenderArg SwitcherView::InterpolateRenderArgs(RenderArg const& start, RenderArg const& end, float progress)
00218 {
00219   // easing
00220   progress = -pow(progress - 1.0f, 2) + 1;
00221 
00222   RenderArg result = end;
00223 
00224   result.x_rotation = start.x_rotation + (end.x_rotation - start.x_rotation) * progress;
00225   result.y_rotation = start.y_rotation + (end.y_rotation - start.y_rotation) * progress;
00226   result.z_rotation = start.z_rotation + (end.z_rotation - start.z_rotation) * progress;
00227 
00228   result.render_center.x = start.render_center.x + (end.render_center.x - start.render_center.x) * progress;
00229   result.render_center.y = start.render_center.y + (end.render_center.y - start.render_center.y) * progress;
00230   result.render_center.z = start.render_center.z + (end.render_center.z - start.render_center.z) * progress;
00231 
00232   result.logical_center = result.render_center;
00233 
00234   return result;
00235 }
00236 
00237 nux::Geometry SwitcherView::InterpolateBackground (nux::Geometry const& start, nux::Geometry const& end, float progress)
00238 {
00239   progress = -pow(progress - 1.0f, 2) + 1;
00240 
00241   nux::Geometry result;
00242 
00243   result.x = start.x + (end.x - start.x) * progress;
00244   result.y = start.y + (end.y - start.y) * progress;
00245   result.width = start.width + (end.width - start.width) * progress;
00246   result.height = start.height + (end.height - start.height) * progress;
00247 
00248   return result;
00249 }
00250 
00251 nux::Geometry SwitcherView::UpdateRenderTargets (nux::Point const& center, timespec const& current)
00252 {
00253   std::vector<Window> xids = model_->DetailXids ();
00254 
00255   int ms_since_change = TimeUtil::TimeDelta(&current, &save_time_);
00256   float progress = MIN (1.0f, (float) ms_since_change / (float) animation_length());
00257 
00258   for (Window window : xids)
00259   {
00260     LayoutWindow::Ptr layout_window (new LayoutWindow (window));
00261 
00262     if (window == model_->DetailSelectionWindow ())
00263       layout_window->alpha = 1.0f * progress;
00264     else
00265       layout_window->alpha = 0.9f * progress;
00266 
00267     render_targets_.push_back (layout_window);
00268   }
00269 
00270   nux::Geometry max_bounds;
00271 
00272   nux::Geometry absolute = GetAbsoluteGeometry ();
00273   nux::Size spread_size = SpreadSize();
00274   max_bounds.x = absolute.x + center.x - spread_size.width / 2;
00275   max_bounds.y = absolute.y + center.y - spread_size.height / 2;
00276   max_bounds.width = spread_size.width;
00277   max_bounds.height = spread_size.height;
00278 
00279   nux::Geometry final_bounds;
00280   layout_system_->LayoutWindows (render_targets_, max_bounds, final_bounds);
00281 
00282   return final_bounds;
00283 }
00284 
00285 void SwitcherView::OffsetRenderTargets (int x, int y)
00286 {
00287   for (LayoutWindow::Ptr target : render_targets_)
00288   {
00289     target->result.x += x;
00290     target->result.y += y;
00291   }
00292 }
00293 
00294 nux::Size SwitcherView::SpreadSize()
00295 {
00296   nux::Geometry base = GetGeometry();
00297   nux::Size result (base.width - border_size * 2, base.height - border_size * 2);
00298 
00299   int width_padding = std::max(model_->Size() - 1, 0) * minimum_spacing + tile_size;
00300   int height_padding = text_size;
00301 
00302   result.width -= width_padding;
00303   result.height -= height_padding;
00304 
00305   return result;
00306 }
00307 
00308 void SwitcherView::GetFlatIconPositions (int n_flat_icons,
00309                                          int size,
00310                                          int selection,
00311                                          int &first_flat,
00312                                          int &last_flat,
00313                                          int &half_fold_left,
00314                                          int &half_fold_right)
00315 {
00316   half_fold_left = -1;
00317   half_fold_right = -1;
00318 
00319   if (n_flat_icons == 0)
00320   {
00321     first_flat = selection + 1;
00322     last_flat = selection;
00323   }
00324   else if (n_flat_icons == 1)
00325   {
00326     if (selection == 0)
00327     {
00328       // selection is first item
00329       first_flat = 0;
00330       last_flat = n_flat_icons;
00331     }
00332     else if (selection >= size - 2)
00333     {
00334       // selection is in ending area where all icons at end should flatten
00335       first_flat = size - n_flat_icons - 1;
00336       last_flat = size - 1;
00337     }
00338     else
00339     {
00340       first_flat = selection;
00341       last_flat = selection;
00342 
00343       half_fold_left = first_flat - 1;
00344       half_fold_right = last_flat + 1;
00345     }
00346   }
00347   else
00348   {
00349     if (selection == 0)
00350     {
00351       // selection is first item
00352       first_flat = 0;
00353       last_flat = n_flat_icons;
00354     }
00355     else if (selection >= 1 && selection <= n_flat_icons - 1)
00356     {
00357       // selection is in "beginning" area before flat section starts moving
00358       // first item should half fold still
00359       first_flat = 1;
00360       last_flat = n_flat_icons;
00361 
00362       half_fold_left = 0;
00363       half_fold_right = last_flat + 1;
00364     }
00365     else if (selection >= size - 2)
00366     {
00367       // selection is in ending area where all icons at end should flatten
00368       first_flat = size - n_flat_icons - 1;
00369       last_flat = size - 1;
00370     }
00371     else
00372     {
00373       first_flat = selection - n_flat_icons + 2;
00374       last_flat = selection + 1;
00375 
00376       half_fold_left = first_flat - 1;
00377       half_fold_right = last_flat + 1;
00378     }
00379   }
00380 }
00381 
00382 std::list<RenderArg> SwitcherView::RenderArgsFlat(nux::Geometry& background_geo, int selection, timespec const& current)
00383 {
00384   std::list<RenderArg> results;
00385   nux::Geometry base = GetGeometry();
00386 
00387   render_targets_.clear ();
00388 
00389   bool detail_selection = model_->detail_selection;
00390 
00391   background_geo.y = base.y + base.height / 2 - (vertical_size / 2);
00392   background_geo.height = vertical_size + text_size;
00393 
00394 
00395   if (model_)
00396   {
00397 
00398     int size = model_->Size();
00399     int padded_tile_size = tile_size + flat_spacing * 2;
00400     int max_width = base.width - border_size * 2;
00401 
00402     nux::Geometry spread_bounds;
00403     int spread_padded_width = 0;
00404     if (detail_selection)
00405     {
00406       spread_bounds = UpdateRenderTargets (nux::Point (0, 0), current);
00407       // remove extra space consumed by spread
00408       spread_padded_width = spread_bounds.width + 100;
00409       max_width -= spread_padded_width - tile_size;
00410 
00411       int expansion = MAX (0, spread_bounds.height - icon_size);
00412       background_geo.y -= expansion / 2;
00413       background_geo.height += expansion;
00414     }
00415 
00416     int flat_width = size * padded_tile_size;
00417 
00418     int n_flat_icons = CLAMP((max_width - size * minimum_spacing) / padded_tile_size - 1, 0, size);
00419 
00420     int overflow = flat_width - max_width;
00421     float x = 0;
00422     if (overflow < 0)
00423     {
00424       background_geo.x = base.x - overflow / 2;
00425       background_geo.width = base.width + overflow;
00426 
00427       x -= overflow / 2;
00428       overflow = 0;
00429     }
00430     else
00431     {
00432       background_geo.x = base.x;
00433       background_geo.width = base.width;
00434     }
00435 
00436     int non_flat_icons = std::max (1.0f, (float)size - n_flat_icons - 1);
00437     float partial_overflow = (float) overflow / (float)non_flat_icons;
00438     float partial_overflow_scalar = (float)(padded_tile_size - partial_overflow) / (float)(padded_tile_size);
00439 
00440     int first_flat, last_flat;
00441     int half_fold_left;
00442     int half_fold_right;
00443 
00444     GetFlatIconPositions (n_flat_icons, size, selection, first_flat, last_flat, half_fold_left, half_fold_right);
00445 
00446     SwitcherModel::iterator it;
00447     int i = 0;
00448     int y = base.y + base.height / 2;
00449     x += border_size;
00450     for (it = model_->begin(); it != model_->end(); ++it)
00451     {
00452       RenderArg arg = CreateBaseArgForIcon(*it);
00453 
00454       float scalar = partial_overflow_scalar;
00455 
00456       bool should_flat = false;
00457 
00458       if (i >= first_flat && i <= last_flat)
00459       {
00460         scalar = 1.0f;
00461         should_flat = true;
00462       }
00463       else if (i == half_fold_left || i == half_fold_right)
00464       {
00465         scalar += (1.0f - scalar) * 0.5f;
00466       }
00467 
00468       int half_size = tile_size / 2;
00469       if (i == selection && detail_selection)
00470       {
00471         half_size = spread_padded_width / 2;
00472         scalar = 1.0f;
00473       }
00474 
00475       x += (half_size + flat_spacing) * scalar;
00476 
00477       if (should_flat)
00478         arg.render_center = nux::Point3((int) x, y, 0);
00479       else
00480         arg.render_center = nux::Point3(x, y, 0);
00481 
00482       x += (half_size + flat_spacing) * scalar;
00483 
00484       arg.y_rotation = (1.0f - MIN (1.0f, scalar));
00485 
00486       if (!should_flat && overflow > 0 && i != selection)
00487       {
00488         if (i > last_flat)
00489         {
00490           arg.render_center.x -= 20;
00491         }
00492         else
00493         {
00494           arg.render_center.x += 20;
00495           arg.y_rotation = -arg.y_rotation;
00496         }
00497       }
00498 
00499       arg.render_center.z = abs(80.0f * arg.y_rotation);
00500       arg.logical_center = arg.render_center;
00501 
00502       if (i == selection && detail_selection)
00503       {
00504         arg.skip = true;
00505         OffsetRenderTargets (arg.render_center.x, arg.render_center.y);
00506       }
00507 
00508       results.push_back(arg);
00509       ++i;
00510     }
00511 
00512     int ms_since_change = TimeUtil::TimeDelta(&current, &save_time_);
00513     if (saved_args_.size () == results.size () && ms_since_change < animation_length)
00514     {
00515       float progress = (float) ms_since_change / (float) animation_length();
00516 
00517       std::list<RenderArg> end = results;
00518       results.clear();
00519 
00520       std::list<RenderArg>::iterator start_it, end_it;
00521       for (start_it = saved_args_.begin(), end_it = end.begin(); start_it != saved_args_.end(); ++start_it, ++end_it)
00522       {
00523         results.push_back(InterpolateRenderArgs(*start_it, *end_it, progress));
00524       }
00525 
00526       background_geo = InterpolateBackground (saved_background_, background_geo, progress);
00527     }
00528   }
00529 
00530   return results;
00531 }
00532 
00533 void SwitcherView::PreDraw(nux::GraphicsEngine& GfxContext, bool force_draw)
00534 {
00535   clock_gettime(CLOCK_MONOTONIC, &current_);
00536 
00537   if (!target_sizes_set_)
00538   {
00539     icon_renderer_->SetTargetSize(tile_size, icon_size, 10);
00540     target_sizes_set_ = true;
00541   }
00542 
00543   nux::Geometry background_geo;
00544   last_args_ = RenderArgsFlat(background_geo, model_->SelectionIndex(), current_);
00545   last_background_ = background_geo;
00546 
00547   icon_renderer_->PreprocessIcons(last_args_, GetGeometry());
00548 }
00549 
00550 nux::Geometry SwitcherView::GetBackgroundGeometry()
00551 {
00552   return last_background_;
00553 }
00554 
00555 void SwitcherView::DrawOverlay(nux::GraphicsEngine& GfxContext, bool force_draw, nux::Geometry internal_clip)
00556 {
00557   nux::Geometry base = GetGeometry();
00558 
00559   GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER);
00560   std::list<RenderArg>::iterator it;
00561   for (it = last_args_.begin(); it != last_args_.end(); ++it)
00562   {
00563     if (model_->Selection() == it->icon)
00564     {
00565       int view_width = text_view_->GetBaseWidth();
00566       int start_x = it->render_center.x - view_width / 2;
00567 
00568       internal_clip.Expand (-10, -10);
00569       if (start_x < internal_clip.x)
00570         start_x = internal_clip.x;
00571       else if (start_x + view_width > internal_clip.x + internal_clip.width)
00572         start_x = (internal_clip.x + internal_clip.width) - view_width;
00573 
00574       text_view_->SetBaseX(start_x);
00575     }
00576     if (it->y_rotation < 0)
00577       icon_renderer_->RenderIcon(GfxContext, *it, base, base);
00578   }
00579 
00580   std::list<RenderArg>::reverse_iterator rit;
00581   for (rit = last_args_.rbegin(); rit != last_args_.rend(); ++rit)
00582   {
00583     if (rit->y_rotation >= 0)
00584       icon_renderer_->RenderIcon(GfxContext, *rit, base, base);
00585   }
00586 
00587   if (render_boxes)
00588   {
00589     float val = 0.1f;
00590     for (LayoutWindow::Ptr layout : ExternalTargets())
00591     {
00592       gPainter.Paint2DQuadColor(GfxContext, layout->result, nux::Color(val, val, val ,val));
00593       val += 0.1f;
00594 
00595       if (val > 1)
00596         val = 0.1f;
00597     }
00598 
00599     for (rit = last_args_.rbegin(); rit != last_args_.rend(); ++rit)
00600     {
00601       nux::Geometry tmp (rit->render_center.x - 1, rit->render_center.y - 1, 2, 2);
00602       gPainter.Paint2DQuadColor(GfxContext, tmp, nux::color::Red);
00603     }
00604   }
00605 
00606   // render orange box that will encirlce active item(s)
00607   for (LayoutWindow::Ptr window : ExternalTargets())
00608   {
00609     nux::Geometry const& geo_absolute = GetAbsoluteGeometry();
00610     if (window->alpha >= 1.0f)
00611     {
00612       nux::Geometry orange_box = window->result;
00613       orange_box.Expand(5, 5);
00614       orange_box.x -= geo_absolute.x;
00615       orange_box.y -= geo_absolute.y;
00616 
00617       gPainter.PaintTextureShape(GfxContext, orange_box, rounding_texture_.GetPointer(), 6, 6, 6, 6, false);
00618     }
00619   }
00620 
00621   text_view_->SetBaseY(last_background_.y + last_background_.height - 45);
00622   text_view_->Draw(GfxContext, force_draw);
00623 
00624   int ms_since_change = TimeUtil::TimeDelta(&current_, &save_time_);
00625 
00626   if (ms_since_change < animation_length && !redraw_idle_)
00627   {
00628     redraw_idle_.reset(new glib::Idle([&] () {
00629       QueueDraw();
00630       animation_draw_ = true;
00631       redraw_idle_.reset();
00632       return false;
00633     }, glib::Source::Priority::DEFAULT));
00634   }
00635 
00636   animation_draw_ = false;
00637 }
00638 
00639 }
00640 }