Back to index

unity  6.0.0
Tooltip.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2010-2012 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: Jay Taoko <jay.taoko@canonical.com>
00018  *              Mirco Müller <mirco.mueller@canonical.com
00019  *              Andrea Cimitan <andrea.cimitan@canonical.com>
00020  *              Marco Trevisan (Treviño) <3v1n0@ubuntu.com>
00021  */
00022 
00023 #include <Nux/Nux.h>
00024 #include <UnityCore/Variant.h>
00025 
00026 #include "unity-shared/CairoTexture.h"
00027 #include "unity-shared/ubus-server.h"
00028 #include "unity-shared/UBusMessages.h"
00029 
00030 #include "Tooltip.h"
00031 
00032 namespace unity
00033 {
00034 namespace
00035 {
00036   const int ANCHOR_WIDTH = 14;
00037   const int ANCHOR_HEIGHT = 18;
00038   const int CORNER_RADIUS = 4;
00039   const int PADDING = 15;
00040   const int TEXT_PADDING = 8;
00041   const int MINIMUM_TEXT_WIDTH = 100;
00042   const int TOP_SIZE = 0;
00043 }
00044 
00045 NUX_IMPLEMENT_OBJECT_TYPE(Tooltip);
00046 Tooltip::Tooltip() :
00047   _anchorX(0),
00048   _anchorY(0),
00049   _labelText(TEXT("Unity")),
00050   _cairo_text_has_changed(true)
00051 {
00052   _use_blurred_background = true;
00053   _compute_blur_bkg = true;
00054 
00055   _hlayout = new nux::HLayout(TEXT(""), NUX_TRACKER_LOCATION);
00056   _vlayout = new nux::VLayout(TEXT(""), NUX_TRACKER_LOCATION);
00057 
00058   _left_space = new nux::SpaceLayout(PADDING + ANCHOR_WIDTH, PADDING + ANCHOR_WIDTH, 1, 1000);
00059   _right_space = new nux::SpaceLayout(PADDING + CORNER_RADIUS, PADDING + CORNER_RADIUS, 1, 1000);
00060 
00061   _top_space = new nux::SpaceLayout(1, 1000, PADDING, PADDING);
00062   _bottom_space = new nux::SpaceLayout(1, 1000, PADDING, PADDING);
00063 
00064   _vlayout->AddLayout(_top_space, 0);
00065 
00066   _tooltip_text = new nux::StaticCairoText(_labelText, NUX_TRACKER_LOCATION);
00067   _tooltip_text->SetTextAlignment(nux::StaticCairoText::AlignState::NUX_ALIGN_CENTRE);
00068   _tooltip_text->SetTextVerticalAlignment(nux::StaticCairoText::AlignState::NUX_ALIGN_CENTRE);
00069   _tooltip_text->SetMinimumWidth(MINIMUM_TEXT_WIDTH);
00070 
00071   _tooltip_text->sigTextChanged.connect(sigc::mem_fun(this, &Tooltip::RecvCairoTextChanged));
00072   _tooltip_text->sigFontChanged.connect(sigc::mem_fun(this, &Tooltip::RecvCairoTextChanged));
00073 
00074   _vlayout->AddView(_tooltip_text.GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);
00075 
00076   _vlayout->AddLayout(_bottom_space, 0);
00077 
00078   _hlayout->AddLayout(_left_space, 0);
00079   _hlayout->AddLayout(_vlayout, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);
00080   _hlayout->AddLayout(_right_space, 0);
00081 
00082   SetWindowSizeMatchLayout(true);
00083   SetLayout(_hlayout);
00084 }
00085 
00086 nux::Area* Tooltip::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type)
00087 {
00088   // No area under mouse to allow click through to entities below
00089   return nullptr;
00090 }
00091 
00092 void Tooltip::ShowTooltipWithTipAt(int anchor_tip_x, int anchor_tip_y)
00093 {
00094   _anchorX = anchor_tip_x;
00095   _anchorY = anchor_tip_y;
00096 
00097   int x = _anchorX - PADDING;
00098   int y = anchor_tip_y - ANCHOR_HEIGHT / 2 - TOP_SIZE - CORNER_RADIUS - PADDING;
00099 
00100   _compute_blur_bkg = true;
00101 
00102   SetBaseX(x);
00103   SetBaseY(y);
00104 
00105   PushToFront();
00106 
00107   ShowWindow(true);
00108   UBusServer* ubus = ubus_server_get_default();
00109   ubus_server_send_message(ubus, UBUS_TOOLTIP_SHOWN, NULL);
00110 }
00111 
00112 void Tooltip::Draw(nux::GraphicsEngine& gfxContext, bool forceDraw)
00113 {
00114   CairoBaseWindow::Draw(gfxContext, forceDraw);
00115   _tooltip_text->ProcessDraw(gfxContext, forceDraw);
00116 }
00117 
00118 void Tooltip::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw)
00119 {}
00120 
00121 void Tooltip::PreLayoutManagement()
00122 {
00123   int text_width;
00124   int text_height;
00125   int text_min_width = MINIMUM_TEXT_WIDTH;
00126 
00127   _tooltip_text->GetTextExtents(text_width, text_height);
00128 
00129   if (text_width + TEXT_PADDING * 2 > text_min_width)
00130   {
00131     text_min_width = text_width + TEXT_PADDING * 2;
00132   }
00133 
00134   _tooltip_text->SetMinimumWidth(text_min_width);
00135 
00136   if (text_height < ANCHOR_HEIGHT)
00137   {
00138     _top_space->SetMinMaxSize(1, (ANCHOR_HEIGHT - text_height) / 2 + PADDING + CORNER_RADIUS);
00139     _bottom_space->SetMinMaxSize(1, (ANCHOR_HEIGHT - text_height) / 2 + 1 + PADDING + CORNER_RADIUS);
00140   }
00141 
00142   CairoBaseWindow::PreLayoutManagement();
00143 }
00144 
00145 long Tooltip::PostLayoutManagement(long LayoutResult)
00146 {
00147   long result = CairoBaseWindow::PostLayoutManagement(LayoutResult);
00148   UpdateTexture();
00149 
00150   return result;
00151 }
00152 
00153 void Tooltip::RecvCairoTextChanged(nux::StaticCairoText* cairo_text)
00154 {
00155   _cairo_text_has_changed = true;
00156 }
00157 
00161 
00162 void tint_dot_hl(cairo_t* cr,
00163                  gint    width,
00164                  gint    height,
00165                  gfloat  hl_x,
00166                  gfloat  hl_y,
00167                  gfloat  hl_size,
00168                  gfloat* rgba_tint,
00169                  gfloat* rgba_hl,
00170                  gfloat* rgba_dot)
00171 {
00172   cairo_pattern_t* hl_pattern = NULL;
00173 
00174   // clear normal context
00175   cairo_scale(cr, 1.0f, 1.0f);
00176   cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f);
00177   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00178   cairo_paint(cr);
00179 
00180   // prepare drawing for normal context
00181   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00182 
00183   // create path in normal context
00184   cairo_rectangle(cr, 0.0f, 0.0f, (gdouble) width, (gdouble) height);
00185 
00186   // fill path of normal context with tint
00187   cairo_set_source_rgba(cr,
00188                         rgba_tint[0],
00189                         rgba_tint[1],
00190                         rgba_tint[2],
00191                         rgba_tint[3]);
00192   cairo_fill_preserve(cr);
00193 
00194   // draw glow
00195   hl_pattern = cairo_pattern_create_radial(hl_x,
00196                                            hl_y - hl_size / 1.4f,
00197                                            0.0f,
00198                                            hl_x,
00199                                            hl_y - hl_size / 1.4f,
00200                                            hl_size);
00201   cairo_pattern_add_color_stop_rgba(hl_pattern,
00202                                     0.0f,
00203                                     rgba_hl[0],
00204                                     rgba_hl[1],
00205                                     rgba_hl[2],
00206                                     rgba_hl[3]);
00207   cairo_pattern_add_color_stop_rgba(hl_pattern, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f);
00208   cairo_set_source(cr, hl_pattern);
00209   cairo_fill(cr);
00210   cairo_pattern_destroy(hl_pattern);
00211 }
00212 
00213 void _setup(cairo_surface_t** surf,
00214             cairo_t**         cr,
00215             gboolean          outline,
00216             gint              width,
00217             gint              height,
00218             gboolean          negative)
00219 {
00220   // clear context
00221   cairo_scale(*cr, 1.0f, 1.0f);
00222   if (outline)
00223   {
00224     cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f);
00225     cairo_set_operator(*cr, CAIRO_OPERATOR_CLEAR);
00226   }
00227   else
00228   {
00229     cairo_set_operator(*cr, CAIRO_OPERATOR_OVER);
00230     if (negative)
00231       cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f);
00232     else
00233       cairo_set_source_rgba(*cr, 1.0f, 1.0f, 1.0f, 1.0f);
00234   }
00235   cairo_paint(*cr);
00236 }
00237 
00238 void _compute_full_mask_path(cairo_t* cr,
00239                              gfloat   anchor_width,
00240                              gfloat   anchor_height,
00241                              gint     width,
00242                              gint     height,
00243                              gint     upper_size,
00244                              gfloat   radius,
00245                              guint    pad)
00246 {
00247 
00248   //     0            1 2
00249   //     +------------+-+
00250   //    /               + 3
00251   //   /                |
00252   //  + 8               |
00253   //   \                |
00254   //    \               + 4
00255   //     +------------+-+
00256   //     7            6 5
00257 
00258   gfloat padding = pad;
00259 
00260   cairo_translate(cr, -0.5f, -0.5f);
00261 
00262   // create path
00263   cairo_move_to(cr, padding + anchor_width, padding); // Point 0
00264   cairo_line_to(cr, width - padding - radius, padding); // Point 1
00265   cairo_arc(cr,
00266             width  - padding - radius,
00267             padding + radius,
00268             radius,
00269             -90.0f * G_PI / 180.0f,
00270             0.0f * G_PI / 180.0f); // Point 3
00271   cairo_line_to(cr,
00272                 (gdouble) width - padding,
00273                 (gdouble) height - radius - padding); // Point 4
00274   cairo_arc(cr,
00275             (gdouble) width - padding - radius,
00276             (gdouble) height - padding - radius,
00277             radius,
00278             0.0f * G_PI / 180.0f,
00279             90.0f * G_PI / 180.0f); // Point 6
00280   cairo_line_to(cr,
00281                 anchor_width + padding,
00282                 (gdouble) height - padding); // Point 7
00283 
00284   cairo_line_to(cr,
00285                 padding,
00286                 (gdouble) height / 2.0f); // Point 8
00287 
00288   cairo_close_path(cr);
00289 }
00290 
00291 void compute_mask(cairo_t* cr)
00292 {
00293   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00294   cairo_fill_preserve(cr);
00295 }
00296 
00297 void compute_outline(cairo_t* cr,
00298                      gfloat   line_width,
00299                      gfloat*  rgba_line)
00300 {
00301   cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
00302   cairo_set_source_rgba(cr,
00303                         rgba_line[0],
00304                         rgba_line[1],
00305                         rgba_line[2],
00306                         rgba_line[3]);
00307   cairo_set_line_width(cr, line_width);
00308   cairo_stroke(cr);
00309 }
00310 
00311 void _draw(cairo_t* cr,
00312            gboolean outline,
00313            gfloat   line_width,
00314            gfloat*  rgba,
00315            gboolean negative,
00316            gboolean stroke)
00317 {
00318   // prepare drawing
00319   cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
00320 
00321   // actually draw the mask
00322   if (outline)
00323   {
00324     cairo_set_line_width(cr, line_width);
00325     cairo_set_source_rgba(cr, rgba[0], rgba[1], rgba[2], rgba[3]);
00326   }
00327   else
00328   {
00329     if (negative)
00330       cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f);
00331     else
00332       cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f);
00333   }
00334 
00335   // stroke or fill?
00336   if (stroke)
00337     cairo_stroke_preserve(cr);
00338   else
00339     cairo_fill_preserve(cr);
00340 }
00341 
00342 void _finalize(cairo_t** cr,
00343                gboolean  outline,
00344                gfloat    line_width,
00345                gfloat*   rgba,
00346                gboolean  negative,
00347                gboolean  stroke)
00348 {
00349   // prepare drawing
00350   cairo_set_operator(*cr, CAIRO_OPERATOR_SOURCE);
00351 
00352   // actually draw the mask
00353   if (outline)
00354   {
00355     cairo_set_line_width(*cr, line_width);
00356     cairo_set_source_rgba(*cr, rgba[0], rgba[1], rgba[2], rgba[3]);
00357   }
00358   else
00359   {
00360     if (negative)
00361       cairo_set_source_rgba(*cr, 1.0f, 1.0f, 1.0f, 1.0f);
00362     else
00363       cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f);
00364   }
00365 
00366   // stroke or fill?
00367   if (stroke)
00368     cairo_stroke(*cr);
00369   else
00370     cairo_fill(*cr);
00371 }
00372 
00373 void
00374 compute_full_outline_shadow(
00375   cairo_t* cr,
00376   cairo_surface_t* surf,
00377   gint    width,
00378   gint    height,
00379   gfloat  anchor_width,
00380   gfloat  anchor_height,
00381   gint    upper_size,
00382   gfloat  corner_radius,
00383   guint   blur_coeff,
00384   gfloat* rgba_shadow,
00385   gfloat  line_width,
00386   gint    padding_size,
00387   gfloat* rgba_line)
00388 {
00389   _setup(&surf, &cr, TRUE, width, height, FALSE);
00390   _compute_full_mask_path(cr,
00391                           anchor_width,
00392                           anchor_height,
00393                           width,
00394                           height,
00395                           upper_size,
00396                           corner_radius,
00397                           padding_size);
00398 
00399   _draw(cr, TRUE, line_width, rgba_shadow, FALSE, FALSE);
00400   nux::CairoGraphics dummy(CAIRO_FORMAT_A1, 1, 1);
00401   dummy.BlurSurface(blur_coeff, surf);
00402   compute_mask(cr);
00403   compute_outline(cr, line_width, rgba_line);
00404 }
00405 
00406 void compute_full_mask(
00407   cairo_t* cr,
00408   cairo_surface_t* surf,
00409   gint     width,
00410   gint     height,
00411   gfloat   radius,
00412   guint    shadow_radius,
00413   gfloat   anchor_width,
00414   gfloat   anchor_height,
00415   gint     upper_size,
00416   gboolean negative,
00417   gboolean outline,
00418   gfloat   line_width,
00419   gint     padding_size,
00420   gfloat*  rgba)
00421 {
00422   _setup(&surf, &cr, outline, width, height, negative);
00423   _compute_full_mask_path(cr,
00424                           anchor_width,
00425                           anchor_height,
00426                           width,
00427                           height,
00428                           upper_size,
00429                           radius,
00430                           padding_size);
00431   _finalize(&cr, outline, line_width, rgba, negative, outline);
00432 }
00433 
00434 void Tooltip::UpdateTexture()
00435 {
00436   if (_cairo_text_has_changed == false)
00437     return;
00438 
00439   int width = GetBaseWidth();
00440   int height = GetBaseHeight();
00441 
00442   int x = _anchorX - PADDING;
00443   int y = _anchorY - height / 2;
00444 
00445   float blur_coef = 6.0f;
00446 
00447   SetBaseX(x);
00448   SetBaseY(y);
00449 
00450   nux::CairoGraphics cairo_bg(CAIRO_FORMAT_ARGB32, width, height);
00451   nux::CairoGraphics cairo_mask(CAIRO_FORMAT_ARGB32, width, height);
00452   nux::CairoGraphics cairo_outline(CAIRO_FORMAT_ARGB32, width, height);
00453 
00454   cairo_t* cr_bg      = cairo_bg.GetContext();
00455   cairo_t* cr_mask    = cairo_mask.GetContext();
00456   cairo_t* cr_outline = cairo_outline.GetContext();
00457 
00458   float   tint_color[4]    = {0.074f, 0.074f, 0.074f, 0.80f};
00459   float   hl_color[4]      = {1.0f, 1.0f, 1.0f, 0.8f};
00460   float   dot_color[4]     = {1.0f, 1.0f, 1.0f, 0.20f};
00461   float   shadow_color[4]  = {0.0f, 0.0f, 0.0f, 1.00f};
00462   float   outline_color[4] = {1.0f, 1.0f, 1.0f, 0.15f};
00463   float   mask_color[4]    = {1.0f, 1.0f, 1.0f, 1.00f};
00464 
00465   tint_dot_hl(cr_bg,
00466               width,
00467               height,
00468               width / 2.0f,
00469               0,
00470               nux::Max<float>(width / 1.3f, height / 1.3f),
00471               tint_color,
00472               hl_color,
00473               dot_color);
00474 
00475   compute_full_outline_shadow
00476   (
00477     cr_outline,
00478     cairo_outline.GetSurface(),
00479     width,
00480     height,
00481     ANCHOR_WIDTH,
00482     ANCHOR_HEIGHT,
00483     -1,
00484     CORNER_RADIUS,
00485     blur_coef,
00486     shadow_color,
00487     1.0f,
00488     PADDING,
00489     outline_color);
00490 
00491   compute_full_mask(
00492     cr_mask,
00493     cairo_mask.GetSurface(),
00494     width,
00495     height,
00496     CORNER_RADIUS, // radius,
00497     16,             // shadow_radius,
00498     ANCHOR_WIDTH,  // anchor_width,
00499     ANCHOR_HEIGHT, // anchor_height,
00500     -1,             // upper_size,
00501     true,           // negative,
00502     false,          // outline,
00503     1.0,            // line_width,
00504     PADDING,       // padding_size,
00505     mask_color);
00506 
00507   cairo_destroy(cr_bg);
00508   cairo_destroy(cr_outline);
00509   cairo_destroy(cr_mask);
00510 
00511   texture_bg_ = texture_ptr_from_cairo_graphics(cairo_bg);
00512   texture_mask_ = texture_ptr_from_cairo_graphics(cairo_mask);
00513   texture_outline_ = texture_ptr_from_cairo_graphics(cairo_outline);
00514 
00515   _cairo_text_has_changed = false;
00516 }
00517 
00518 void Tooltip::PositionChildLayout(float offsetX,
00519                                   float offsetY)
00520 {
00521 }
00522 
00523 void Tooltip::LayoutWindowElements()
00524 {
00525 }
00526 
00527 void Tooltip::NotifyConfigurationChange(int width,
00528                                         int height)
00529 {
00530 }
00531 
00532 void Tooltip::SetText(std::string const& text)
00533 {
00534   if (_labelText == text)
00535     return;
00536 
00537   _labelText = text;
00538   _tooltip_text->SetText(_labelText);
00539 
00540   QueueRelayout();
00541 }
00542 
00543 // Introspection
00544 
00545 std::string Tooltip::GetName() const
00546 {
00547   return "ToolTip";
00548 }
00549 
00550 void Tooltip::AddProperties(GVariantBuilder* builder)
00551 {
00552   variant::BuilderWrapper(builder)
00553     .add("text", _labelText)
00554     .add("x", GetBaseX())
00555     .add("y", GetBaseY())
00556     .add("width", GetBaseWidth())
00557     .add("height", GetBaseHeight())
00558     .add("active", IsVisible());
00559 }
00560 
00561 } // namespace nux