Back to index

unity  6.0.0
LayoutSystem.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2011 Canonical Ltd
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 3 as
00007  * published by the Free Software Foundation.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * Authored by: Jason Smith <jason.smith@canonical.com>
00018  */
00019 
00020 #include "LayoutSystem.h"
00021 #include "unity-shared/WindowManager.h"
00022 
00023 namespace unity {
00024 namespace ui {
00025        
00026 LayoutSystem::LayoutSystem()
00027 {
00028   spacing = 8;
00029   max_row_height = 400;
00030 }
00031 
00032 LayoutSystem::~LayoutSystem()
00033 {
00034 }
00035 
00036 void LayoutSystem::LayoutWindows (LayoutWindowList windows, nux::Geometry const& max_bounds, nux::Geometry& final_bounds)
00037 {
00038   unsigned int size = windows.size();
00039 
00040   if (size == 0)
00041     return;
00042   
00043   for (auto window : windows)
00044   {
00045        window->geo = WindowManager::Default ()->GetWindowGeometry (window->xid);
00046     window->aspect_ratio = (float)window->geo.width / (float)window->geo.height;
00047   }
00048   
00049   LayoutGridWindows (windows, max_bounds, final_bounds);
00050 }
00051 
00052 nux::Size LayoutSystem::GridSizeForWindows (LayoutWindowList windows, nux::Geometry const& max_bounds)
00053 {
00054   int count = (int)windows.size();
00055 
00056   int width = 1;
00057   int height = 1;
00058 
00059   if (count == 2)
00060   {
00061     float stacked_aspect = std::max (windows[0]->geo.width, windows[1]->geo.width) / (float)(windows[0]->geo.height + windows[1]->geo.height);  
00062     float row_aspect = (float)(windows[0]->geo.width + windows[1]->geo.width) / std::max(windows[0]->geo.height, windows[1]->geo.height);
00063     float box_aspect = (float)max_bounds.width / max_bounds.height;
00064     if (abs(row_aspect - box_aspect) > abs(stacked_aspect - box_aspect))
00065     {
00066       height = 2;
00067     }
00068     else
00069     {
00070       width = 2;
00071     }
00072   }
00073   else
00074   {
00075     while (width * height < count)
00076     {
00077       if (height < width)
00078         height++;
00079       else
00080         width++;
00081     }
00082   }
00083 
00084   return nux::Size (width, height);
00085 }
00086 
00087 nux::Geometry LayoutSystem::CompressAndPadRow (LayoutWindowList const& windows, nux::Geometry const& max_bounds)
00088 {
00089   int total_width = 0;
00090   int max_height = 0;
00091   for (LayoutWindow::Ptr window : windows)
00092   {
00093     window->result.x = total_width;
00094     total_width += spacing + window->result.width;
00095     max_height = std::max (window->result.height, max_height);
00096   }
00097 
00098   total_width -= spacing;
00099 
00100   int x1 = G_MAXINT;
00101   int y1 = G_MAXINT;
00102   int x2 = G_MININT;
00103   int y2 = G_MININT;
00104 
00105   int offset = std::max (0, (max_bounds.width - total_width) / 2);
00106   for (LayoutWindow::Ptr window : windows)
00107   {
00108     window->result.x += max_bounds.x + offset;
00109     window->result.y = max_bounds.y + (max_height - window->result.height) / 2;
00110     
00111     x1 = std::min (window->result.x, x1);
00112     y1 = std::min (window->result.y, y1);
00113     x2 = std::max (window->result.x + window->result.width, x2);
00114     y2 = std::max (window->result.y + window->result.height, y2);
00115   }
00116 
00117   return nux::Geometry (x1, y1, x2 - x1, y2 - y1);
00118 }
00119 
00120 nux::Geometry LayoutSystem::LayoutRow (LayoutWindowList const& row, nux::Geometry const& row_bounds)
00121 {
00122   nux::Geometry unpadded_bounds = row_bounds;
00123   unpadded_bounds.width -= spacing * (row.size () - 1);
00124 
00125   int combined_width = 0;
00126   for (LayoutWindow::Ptr window : row)
00127   {
00128     float scalar = unpadded_bounds.height / (float)window->geo.height;
00129     combined_width += window->geo.width * scalar;
00130   }
00131 
00132   float global_scalar = std::min (1.0f, unpadded_bounds.width / (float)combined_width);
00133 
00134   int x = unpadded_bounds.x;
00135   int y = unpadded_bounds.y;
00136 
00137   // precision of X,Y is relatively unimportant as the Compression stage will fix any issues, sizing
00138   // is however set at this point.
00139   for (LayoutWindow::Ptr window : row)
00140   {
00141     // we dont allow scaling up
00142     float final_scalar = std::min (1.0f, (unpadded_bounds.height / (float)window->geo.height) * global_scalar);
00143     
00144     window->result.x = x;
00145     window->result.y = y;
00146     window->result.width = window->geo.width * final_scalar;
00147     window->result.height = window->geo.height * final_scalar;
00148 
00149     x += window->result.width;
00150   }
00151 
00152   return CompressAndPadRow (row, row_bounds);
00153 }
00154 
00155 std::vector<LayoutWindowList> LayoutSystem::GetRows (LayoutWindowList const& windows, nux::Geometry const& max_bounds)
00156 {
00157   std::vector<LayoutWindowList> rows;
00158 
00159   int size = (int)windows.size();
00160 
00161   float total_aspect = 0;
00162   for (LayoutWindow::Ptr window : windows)
00163   {
00164     total_aspect += window->aspect_ratio;
00165   }
00166 
00167   if (total_aspect < 1.8f * ((float)max_bounds.width / max_bounds.height))
00168   {
00169     // If the total aspect ratio is < 1.8 the max, we fairly safely assume a double row configuration wont be better
00170     rows.push_back(windows);
00171   }
00172   else
00173   {
00174     nux::Size grid_size = GridSizeForWindows (windows, max_bounds);
00175 
00176     int width = grid_size.width;
00177     int height = grid_size.height;
00178 
00179     float row_height = std::min (max_bounds.height / height, max_row_height());
00180     float ideal_aspect = (float)max_bounds.width / row_height;
00181 
00182     int x = 0;
00183     int y = 0;
00184 
00185     int spare_slots = (width * height) - size;
00186 
00187     float row_aspect = 0.0f;
00188 
00189     LayoutWindowList row_accum;
00190     
00191     int i;
00192     for (i = 0; i < size; ++i)
00193     {
00194       LayoutWindow::Ptr window = windows[i];
00195 
00196       row_accum.push_back (window);
00197       row_aspect += window->aspect_ratio;
00198       
00199       ++x;
00200       if (x == width - 1 && spare_slots)
00201       {
00202         bool skip = false;
00203 
00204         if (spare_slots == height - y)
00205           skip = true;
00206         else if (i < size - 1)
00207           skip = row_aspect + windows[i+1]->aspect_ratio >= ideal_aspect;
00208 
00209         if (skip)
00210         {
00211           ++x;
00212           spare_slots--;
00213         }
00214       }
00215 
00216       if (x >= width)
00217       {
00218         // end of row
00219         x = 0;
00220         ++y;
00221         row_aspect = 0;
00222 
00223         rows.push_back(row_accum);
00224         row_accum.clear ();
00225       }
00226     }
00227 
00228     if (!row_accum.empty())
00229       rows.push_back(row_accum);
00230   }
00231 
00232   return rows;
00233 }
00234 
00235 void LayoutSystem::LayoutGridWindows (LayoutWindowList const& windows, nux::Geometry const& max_bounds, nux::Geometry& final_bounds)
00236 {
00237   std::vector<LayoutWindowList> rows = GetRows(windows, max_bounds);
00238   
00239   int height = rows.size();
00240   int non_spacing_height = max_bounds.height - ((height - 1) * spacing);
00241   int row_height = std::min (max_row_height(), non_spacing_height / height);
00242   int start_y = max_bounds.y;
00243   int low_y = 0;
00244 
00245   for (LayoutWindowList row : rows)
00246   {
00247     nux::Geometry row_max_bounds (max_bounds.x, start_y, max_bounds.width, row_height);
00248     nux::Geometry row_final_bounds = LayoutRow (row, row_max_bounds);
00249 
00250     low_y = row_final_bounds.y + row_final_bounds.height;
00251 
00252     start_y += row_final_bounds.height + spacing;
00253   }
00254 
00255   int x1 = G_MAXINT;
00256   int y1 = G_MAXINT;
00257   int x2 = G_MININT;
00258   int y2 = G_MININT;
00259 
00260   int offset = (max_bounds.height - (low_y - max_bounds.y)) / 2;
00261   for (auto window : windows)
00262   {
00263     window->result.y += offset;
00264 
00265        x1 = std::min (window->result.x, x1);
00266     y1 = std::min (window->result.y, y1);
00267     x2 = std::max (window->result.x + window->result.width, x2);
00268     y2 = std::max (window->result.y + window->result.height, y2);
00269   }
00270 
00271   final_bounds = nux::Geometry (x1, y1, x2 - x1, y2 - y1);
00272 }
00273 
00274 LayoutWindow::LayoutWindow(Window xid)
00275  : xid (xid)
00276 {
00277 
00278 }
00279 
00280 }
00281 }