Merge lp:~3v1n0/unity/scale-close-middle-click into lp:unity

Proposed by Marco Trevisan (Treviño) on 2012-09-24
Status: Merged
Approved by: Brandon Schaefer on 2012-09-24
Approved revision: 2737
Merged at revision: 2748
Proposed branch: lp:~3v1n0/unity/scale-close-middle-click
Merge into: lp:unity
Diff against target: 1018 lines (+472/-175)
6 files modified
plugins/unityshell/src/ScreenIntrospection.h (+48/-0)
plugins/unityshell/src/unityshell.cpp (+223/-156)
plugins/unityshell/src/unityshell.h (+23/-19)
tests/autopilot/unity/emulators/screen.py (+42/-0)
tests/autopilot/unity/tests/__init__.py (+12/-0)
tests/autopilot/unity/tests/test_spread.py (+124/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/scale-close-middle-click
Reviewer Review Type Date Requested Status
Brandon Schaefer (community) 2012-09-24 Approve on 2012-09-24
Review via email: mp+126044@code.launchpad.net

Commit Message

UnityShell: enable closing on middle-click, fix scaled decoration sizes, fix theming issues, added scale autopilot tests

Description of the Change

Improved the computation of an unity scaled window size, so that there won't be drawing issues, updated the corner radius value to match the system default, fix style issues on theme changed. Plus, lots of code cleanup and optimizations.

Added introspection for the UnityScreen and UnityWindows so that we can now use autopilot for them.
So, new AP tests for new scale features.

To post a comment you must log in.
Brandon Schaefer (brandontschaefer) wrote :

Code looks good, tests work, bugs look fixed and also works manually :). +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'plugins/unityshell/src/ScreenIntrospection.h'
2--- plugins/unityshell/src/ScreenIntrospection.h 1970-01-01 00:00:00 +0000
3+++ plugins/unityshell/src/ScreenIntrospection.h 2012-09-24 16:14:49 +0000
4@@ -0,0 +1,48 @@
5+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
6+/*
7+ * Copyright (C) 2012 Canonical Ltd
8+ *
9+ * This program is free software: you can redistribute it and/or modify
10+ * it under the terms of the GNU General Public License version 3 as
11+ * published by the Free Software Foundation.
12+ *
13+ * This program is distributed in the hope that it will be useful,
14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+ * GNU General Public License for more details.
17+ *
18+ * You should have received a copy of the GNU General Public License
19+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
20+ *
21+ * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com>
22+ */
23+
24+#ifndef SCREEN_INTROSPECTION_H
25+#define SCREEN_INTROSPECTION_H
26+
27+#include "Introspectable.h"
28+#include <core/core.h>
29+
30+namespace unity
31+{
32+namespace debug
33+{
34+
35+class ScreenIntrospection : public Introspectable
36+{
37+public:
38+ ScreenIntrospection(CompScreen* screen);
39+
40+protected:
41+ std::string GetName() const;
42+ void AddProperties(GVariantBuilder* builder);
43+ IntrospectableList GetIntrospectableChildren();
44+
45+private:
46+ CompScreen* screen_;
47+};
48+
49+}
50+}
51+
52+#endif
53
54=== modified file 'plugins/unityshell/src/unityshell.cpp'
55--- plugins/unityshell/src/unityshell.cpp 2012-09-21 21:52:27 +0000
56+++ plugins/unityshell/src/unityshell.cpp 2012-09-24 16:14:49 +0000
57@@ -26,6 +26,8 @@
58 #include <Nux/WindowCompositor.h>
59 #include <Nux/NuxTimerTickSource.h>
60
61+#include <UnityCore/Variant.h>
62+
63 #include "BaseWindowRaiserImp.h"
64 #include "IconRenderer.h"
65 #include "Launcher.h"
66@@ -125,7 +127,6 @@
67 , allowWindowPaint(false)
68 , _key_nav_mode_requested(false)
69 , _last_output(nullptr)
70- , _bghash(NULL)
71 , grab_index_ (0)
72 , painting_tray_ (false)
73 , last_scroll_event_(0)
74@@ -133,7 +134,7 @@
75 , panel_texture_has_changed_(true)
76 , paint_panel_(false)
77 , scale_just_activated_(false)
78- , minimize_speed_controller(new WindowMinimizeSpeedController())
79+ , screen_introspection_(screen)
80 {
81 Timer timer;
82 #ifndef USE_GLES
83@@ -244,7 +245,7 @@
84 // _bghash is a pointer. We don't want it to be created before Nux system has had a chance
85 // to start. BGHash relies on animations. Nux animation system starts after the WindowThread
86 // has been created.
87- _bghash = new BGHash();
88+ _bghash.reset(new BGHash());
89
90 unity_a11y_init(wt.get());
91
92@@ -375,13 +376,15 @@
93 Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());;
94 XSelectInput(display, GDK_ROOT_WINDOW(), PropertyChangeMask);
95 LOG_INFO(logger) << "UnityScreen constructed: " << timer.ElapsedSeconds() << "s";
96+
97+ panel::Style::Instance().changed.connect(sigc::mem_fun(this, &UnityScreen::OnPanelStyleChanged));
98+
99+ minimize_speed_controller_.DurationChanged.connect(
100+ sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged)
101+ );
102+
103+ AddChild(&screen_introspection_);
104 }
105-
106- panel::Style::Instance().changed.connect(sigc::mem_fun(this, &UnityScreen::OnPanelStyleChanged));
107-
108- minimize_speed_controller->DurationChanged.connect(
109- sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged)
110- );
111 }
112
113 UnityScreen::~UnityScreen()
114@@ -391,7 +394,6 @@
115 unity_a11y_finalize();
116 ::unity::ui::IconRenderer::DestroyTextures();
117 QuicklistManager::Destroy();
118- delete _bghash;
119
120 reset_glib_logging();
121 }
122@@ -659,10 +661,15 @@
123 wy = y + (last_bound.height - height) / 2;
124 }
125
126-void
127-UnityScreen::OnPanelStyleChanged()
128+void UnityScreen::OnPanelStyleChanged()
129 {
130 panel_texture_has_changed_ = true;
131+
132+ // Reload the windows themed textures
133+ UnityWindow::CleanupSharedTextures();
134+
135+ if (WindowManager::Default()->IsScaleActive())
136+ UnityWindow::SetupSharedTextures();
137 }
138
139 void UnityScreen::paintDisplay()
140@@ -1136,6 +1143,12 @@
141 DoAddDamage();
142 handled = true;
143 }
144+ else if (event->xbutton.button == Button2 &&
145+ GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))
146+ {
147+ middle_clicked_ = true;
148+ handled = true;
149+ }
150 break;
151
152 case ButtonRelease:
153@@ -1155,6 +1168,18 @@
154
155 handled = true;
156 }
157+
158+ if (middle_clicked_)
159+ {
160+ if (event->xbutton.button == Button2 &&
161+ GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))
162+ {
163+ window->close(0);
164+ }
165+
166+ middle_clicked_ = false;
167+ handled = true;
168+ }
169 }
170 break;
171
172@@ -2277,8 +2302,7 @@
173 }
174
175 void UnityScreen::AddProperties(GVariantBuilder* builder)
176-{
177-}
178+{}
179
180 std::string UnityScreen::GetName() const
181 {
182@@ -2469,10 +2493,10 @@
183 CompOption::Value& value = o.value();
184 CompOption::Value::Vector& list = value.list();
185 CompOption::Value::Vector::iterator i = list.begin();
186- if (i != list.end()) {
187- i->set(minimize_speed_controller->getDuration());
188- }
189- value.set(list);
190+ if (i != list.end())
191+ i->set(minimize_speed_controller_.getDuration());
192+
193+ value.set(list);
194 screen->setOptionForPlugin(p->vTable->name().c_str(),
195 o.name().c_str(), value);
196 break;
197@@ -2603,7 +2627,7 @@
198 case CompWindowNotifyMinimize:
199 /* Updating the count in dconf will trigger a "changed" signal to which
200 * the method setting the new animation speed is attached */
201- UnityScreen::get(screen)->minimize_speed_controller->UpdateCount();
202+ UnityScreen::get(screen)->minimize_speed_controller_.UpdateCount();
203 break;
204 default:
205 break;
206@@ -3458,107 +3482,117 @@
207 WindowManager::Default()->terminate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnTerminateSpreed));
208 }
209
210-void
211-UnityWindow::DrawTexture(GLTexture* icon,
212- const GLWindowPaintAttrib& attrib,
213- const GLMatrix& transform,
214- unsigned int mask,
215- float x, float y,
216- int &maxWidth, int &maxHeight)
217-{
218- if (icon)
219+void UnityWindow::AddProperties(GVariantBuilder* builder)
220+{
221+ Window xid = window->id();
222+ auto const& swins = ScaleScreen::get(screen)->getWindows();
223+ bool scaled = std::find(swins.begin(), swins.end(), ScaleWindow::get(window)) != swins.end();
224+ auto wm = WindowManager::Default();
225+
226+ variant::BuilderWrapper(builder)
227+ .add(scaled ? GetScaledGeometry() : wm->GetWindowGeometry(xid))
228+ .add("xid", xid)
229+ .add("title", wm->GetWindowName(xid))
230+ .add("scaled", scaled)
231+ .add("scaled_close_x", close_button_geo_.x)
232+ .add("scaled_close_y", close_button_geo_.y)
233+ .add("scaled_close_width", close_button_geo_.width)
234+ .add("scaled_close_height", close_button_geo_.height);
235+}
236+
237+std::string UnityWindow::GetName() const
238+{
239+ return "Window";
240+}
241+
242+void UnityWindow::DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const& attrib,
243+ GLMatrix const& transform, unsigned int mask, int x, int y)
244+{
245+ for (auto const& texture : textures)
246 {
247- int width, height;
248- width = icon->width();
249- height = icon->height();
250-
251- if (height > maxHeight)
252- maxHeight = height;
253-
254- if (width > maxWidth)
255- maxWidth = width;
256-
257- CompRegion iconReg(0, 0, width, height);
258- GLTexture::MatrixList ml(1);
259-
260- ml[0] = icon->matrix();
261+ if (!texture)
262+ continue;
263+
264 gWindow->vertexBuffer()->begin();
265- if (width && height)
266- gWindow->glAddGeometry(ml, iconReg, iconReg);
267+
268+ if (texture->width() && texture->height())
269+ {
270+ GLTexture::MatrixList ml(1);
271+ ml[0] = texture->matrix();
272+ CompRegion texture_region(0, 0, texture->width(), texture->height());
273+ gWindow->glAddGeometry(ml, texture_region, texture_region);
274+ }
275
276 if (gWindow->vertexBuffer()->end())
277 {
278 GLMatrix wTransform(transform);
279-
280 wTransform.translate(x, y, 0.0f);
281
282- gWindow->glDrawTexture(icon, wTransform, attrib, mask);
283+ gWindow->glDrawTexture(texture, wTransform, attrib, mask);
284 }
285 }
286 }
287
288-void
289-UnityWindow::RenderText(UnityWindow::CairoContext const& context,
290- float x, float y,
291- float maxWidth, float maxHeight)
292+void UnityWindow::RenderText(CairoContext const& context, int x, int y, int width, int height)
293 {
294 panel::Style& style = panel::Style::Instance();
295- std::string fontDescription(style.GetFontDescription(panel::PanelItem::TITLE));
296+ std::string const& font_desc = style.GetFontDescription(panel::PanelItem::TITLE);
297
298 glib::Object<PangoLayout> layout(pango_cairo_create_layout(context.cr_));
299- std::shared_ptr<PangoFontDescription> font(pango_font_description_from_string(fontDescription.c_str()),
300+ std::shared_ptr<PangoFontDescription> font(pango_font_description_from_string(font_desc.c_str()),
301 pango_font_description_free);
302
303 pango_layout_set_font_description(layout, font.get());
304
305- GdkScreen* gdkScreen = gdk_screen_get_default();
306- PangoContext* pCxt = pango_layout_get_context(layout);
307+ GdkScreen* gdk_screen = gdk_screen_get_default();
308+ PangoContext* pango_ctx = pango_layout_get_context(layout);
309 int dpi = style.GetTextDPI();
310
311- pango_cairo_context_set_font_options(pCxt, gdk_screen_get_font_options(gdkScreen));
312- pango_cairo_context_set_resolution(pCxt, dpi / static_cast<float>(PANGO_SCALE));
313+ pango_cairo_context_set_font_options(pango_ctx, gdk_screen_get_font_options(gdk_screen));
314+ pango_cairo_context_set_resolution(pango_ctx, dpi / static_cast<float>(PANGO_SCALE));
315 pango_layout_context_changed(layout);
316
317- pango_layout_set_height(layout, maxHeight);
318+ std::string const& win_title = WindowManager::Default()->GetWindowName(window->id());
319+ pango_layout_set_height(layout, height);
320 pango_layout_set_width(layout, -1); //avoid wrap lines
321 pango_layout_set_auto_dir(layout, false);
322- pango_layout_set_text(layout,
323- WindowManager::Default()->GetWindowName(window->id()).c_str(),
324- -1);
325+ pango_layout_set_text(layout, win_title.c_str(), -1);
326
327 /* update the size of the pango layout */
328 pango_cairo_update_layout(context.cr_, layout);
329- cairo_set_operator(context.cr_, CAIRO_OPERATOR_OVER);
330- cairo_set_source_rgba(context.cr_,
331- 1.0,
332- 1.0,
333- 1.0,
334- 1.0);
335+
336+ GtkStyleContext* style_context = style.GetStyleContext();
337+ gtk_style_context_save(style_context);
338+
339+ std::shared_ptr<GtkWidgetPath> widget_path(gtk_widget_path_new(), gtk_widget_path_free);
340+ gtk_widget_path_append_type(widget_path.get(), GTK_TYPE_MENU_BAR);
341+ gtk_widget_path_append_type(widget_path.get(), GTK_TYPE_MENU_ITEM);
342+ gtk_widget_path_iter_set_name(widget_path.get(), -1 , "UnityPanelWidget");
343+
344+ gtk_style_context_set_path(style_context, widget_path.get());
345+ gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_MENUBAR);
346+ gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_MENUITEM);
347
348 // alignment
349 PangoRectangle lRect;
350- int textWidth, textHeight;
351-
352- pango_layout_get_extents(layout, NULL, &lRect);
353- textWidth = lRect.width / PANGO_SCALE;
354- textHeight = lRect.height / PANGO_SCALE;
355-
356- y = ((maxHeight - textHeight) / 2.0) + y;
357- cairo_translate(context.cr_, x, y);
358-
359- if (textWidth > maxWidth)
360+ pango_layout_get_extents(layout, nullptr, &lRect);
361+ int text_width = lRect.width / PANGO_SCALE;
362+ int text_height = lRect.height / PANGO_SCALE;
363+ y += (height - text_height) / 2.0f;
364+ int text_space = width - x;
365+
366+ if (text_width > text_space)
367 {
368- // apply a fade effect in the right corner
369- const int outPixels = textWidth - maxWidth;
370- const int fadingPixels = 35;
371- const int fadingWidth = outPixels < fadingPixels ? outPixels : fadingPixels;
372+ // Cut the text with fade
373+ int out_pixels = text_width - text_space;
374+ const int fading_pixels = 35;
375+ int fading_width = (out_pixels < fading_pixels) ? out_pixels : fading_pixels;
376
377 cairo_push_group(context.cr_);
378- pango_cairo_show_layout(context.cr_, layout);
379+ gtk_render_layout(style_context, context.cr_, x, y, layout);
380 cairo_pop_group_to_source(context.cr_);
381
382- std::shared_ptr<cairo_pattern_t> linpat(cairo_pattern_create_linear(maxWidth - fadingWidth,
383- y, maxWidth, y),
384+ std::shared_ptr<cairo_pattern_t> linpat(cairo_pattern_create_linear(width - fading_width, y, width, y),
385 cairo_pattern_destroy);
386 cairo_pattern_add_color_stop_rgba(linpat.get(), 0, 0, 0, 0, 1);
387 cairo_pattern_add_color_stop_rgba(linpat.get(), 1, 0, 0, 0, 0);
388@@ -3566,61 +3600,55 @@
389 }
390 else
391 {
392- pango_cairo_show_layout(context.cr_, layout);
393+ gtk_render_layout(style_context, context.cr_, x, y, layout);
394 }
395+
396+ gtk_style_context_restore(style_context);
397 }
398
399-void
400-UnityWindow::DrawWindowDecoration(GLWindowPaintAttrib const& attrib,
401- GLMatrix const& transform,
402- unsigned int mask,
403- bool highlighted,
404- int x, int y, unsigned width, unsigned height)
405+void UnityWindow::DrawWindowDecoration(GLWindowPaintAttrib const& attrib,
406+ GLMatrix const& transform,
407+ unsigned int mask,
408+ bool highlighted,
409+ int x, int y, unsigned width, unsigned height)
410 {
411 // Paint a fake window decoration
412 CairoContext context(width, height);
413
414 cairo_save(context.cr_);
415+
416+ // Draw window decoration based on gtk style
417 cairo_push_group(context.cr_);
418+ auto& style = panel::Style::Instance();
419+ gtk_render_background(style.GetStyleContext(), context.cr_, 0, 0, width, height);
420+ gtk_render_frame(style.GetStyleContext(), context.cr_, 0, 0, width, height);
421+ cairo_pop_group_to_source(context.cr_);
422
423 // Round window decoration top border
424- const double aspect = 1.0;
425- const double corner_radius = height / 10.0;
426- const double radius = corner_radius / aspect;
427- const double degrees = M_PI / 180.0;
428+ const double aspect = ScaleWindow::get(window)->getCurrentPosition().scale;
429+ const double radius = 8.0 * aspect;
430
431 cairo_new_sub_path(context.cr_);
432-
433- cairo_arc(context.cr_, radius, radius, radius, 180 * degrees, 270 * degrees);
434- cairo_arc(context.cr_, width - radius, radius, radius, -90 * degrees, 0 * degrees);
435+ cairo_line_to(context.cr_, 0, height);
436+ cairo_arc(context.cr_, radius, radius, radius, M_PI, -M_PI * 0.5f);
437+ cairo_line_to(context.cr_, width - radius, 0);
438+ cairo_arc(context.cr_, width - radius, radius, radius, M_PI * 0.5f, 0);
439 cairo_line_to(context.cr_, width, height);
440- cairo_line_to(context.cr_, 0, height);
441-
442 cairo_close_path(context.cr_);
443- cairo_clip(context.cr_);
444-
445- // Draw window decoration based on gtk style
446- auto& style = panel::Style::Instance();
447- gtk_render_background(style.GetStyleContext(), context.cr_, 0, 0, width, height);
448- gtk_render_frame(style.GetStyleContext(), context.cr_, 0, 0, width, height);
449-
450- cairo_pop_group_to_source(context.cr_);
451-
452- cairo_paint_with_alpha(context.cr_, 1.0);
453+
454+ cairo_fill(context.cr_);
455+
456 cairo_restore(context.cr_);
457
458 if (highlighted)
459 {
460 // Draw windows title
461 const float xText = SCALE_ITEMS_PADDING * 2 + SCALE_CLOSE_ICON_SIZE;
462- RenderText(context, xText, 0.0, width - xText - SCALE_ITEMS_PADDING, height);
463+ RenderText(context, xText, 0.0, width - SCALE_ITEMS_PADDING, height);
464 }
465
466 mask |= PAINT_WINDOW_BLEND_MASK;
467- int maxWidth, maxHeight;
468-
469- for (GLTexture *icon : context.texture_)
470- DrawTexture(icon, attrib, transform, mask, x, y, maxWidth , maxHeight);
471+ DrawTexture(context.texture_, attrib, transform, mask, x, y);
472 }
473
474 void UnityWindow::LoadCloseIcon(panel::WindowState state, GLTexture::List& texture)
475@@ -3631,12 +3659,12 @@
476 auto& style = panel::Style::Instance();
477 auto const& files = style.GetWindowButtonFileNames(panel::WindowButtonType::CLOSE, state);
478
479- CompString pName("unityshell");
480+ CompString plugin("unityshell");
481 for (std::string const& file : files)
482 {
483- CompString fileName(file.c_str());
484+ CompString file_name = file;
485 CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE);
486- texture = GLTexture::readImageToTexture(fileName, pName, size);
487+ texture = GLTexture::readImageToTexture(file_name, plugin, size);
488 if (!texture.empty())
489 break;
490 }
491@@ -3649,49 +3677,57 @@
492 else if (state == panel::WindowState::PRESSED)
493 suffix = "_pressed";
494
495- CompString fileName((PKGDATADIR"/close_dash" + suffix + ".png").c_str());
496+ CompString file_name(PKGDATADIR"/close_dash" + suffix + ".png");
497 CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE);
498- texture = GLTexture::readImageToTexture(fileName, pName, size);
499+ texture = GLTexture::readImageToTexture(file_name, plugin, size);
500 }
501 }
502
503-void UnityWindow::SetupScaleHeaderStyle()
504+void UnityWindow::SetupSharedTextures()
505 {
506 LoadCloseIcon(panel::WindowState::NORMAL, close_normal_tex_);
507 LoadCloseIcon(panel::WindowState::PRELIGHT, close_prelight_tex_);
508 LoadCloseIcon(panel::WindowState::PRESSED, close_pressed_tex_);
509 }
510
511+void UnityWindow::CleanupSharedTextures()
512+{
513+ close_normal_tex_.clear();
514+ close_prelight_tex_.clear();
515+ close_pressed_tex_.clear();
516+}
517+
518 void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib,
519 GLMatrix const& transform,
520 CompRegion const& region,
521 unsigned int mask)
522 {
523 ScaleWindow *scale_win = ScaleWindow::get(window);
524- if (!scale_win)
525- return;
526-
527 scale_win->scalePaintDecoration(attrib, transform, region, mask);
528
529 if (!scale_win->hasSlot()) // animation not finished
530 return;
531
532 ScaleScreen* ss = ScaleScreen::get(screen);
533+ auto state = ss->getState();
534+
535+ if (state != ScaleScreen::Wait && state != ScaleScreen::Out)
536+ return;
537+
538+ auto const& scaled_geo = GetScaledGeometry();
539+ auto const& decoration_extents = window->border();
540+ auto const& pos = scale_win->getCurrentPosition();
541+
542 const bool highlighted = (ss->getSelectedWindow() == window->id());
543-
544- ScalePosition const& pos = scale_win->getCurrentPosition();
545- auto const& border_rect = window->borderRect();
546- auto const& deco_ext = window->border();
547-
548- const unsigned decoration_height = deco_ext.top;
549- unsigned width = (border_rect.width() + deco_ext.left + deco_ext.right) * pos.scale;
550- unsigned height = decoration_height * pos.scale;
551- int x = pos.x() + border_rect.x();
552- int y = pos.y() + border_rect.y() + decoration_height - height - 1;
553-
554- // If window is highlighted, we draw the decoration at full size
555- if (highlighted)
556- height = decoration_height;
557+ int width = scaled_geo.width;
558+ int height = decoration_extents.top;
559+ int x = scaled_geo.x;
560+ int y = scaled_geo.y;
561+
562+
563+ // If window is not highlighted, we draw the decoration at scaled size
564+ if (!highlighted)
565+ height *= pos.scale;
566
567 DrawWindowDecoration(attrib, transform, mask, highlighted, x, y, width, height);
568
569@@ -3699,30 +3735,25 @@
570 {
571 x += SCALE_ITEMS_PADDING;
572 y += (height - SCALE_CLOSE_ICON_SIZE) / 2.0f;
573- int max_height = 0;
574- int max_width = 0;
575 mask |= PAINT_WINDOW_BLEND_MASK;
576
577- switch(close_icon_state_)
578+ switch (close_icon_state_)
579 {
580 case panel::WindowState::NORMAL:
581 default:
582- for (GLTexture *icon : close_normal_tex_)
583- DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height);
584+ DrawTexture(close_normal_tex_, attrib, transform, mask, x, y);
585 break;
586
587 case panel::WindowState::PRELIGHT:
588- for (GLTexture *icon : close_prelight_tex_)
589- DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height);
590+ DrawTexture(close_prelight_tex_, attrib, transform, mask, x, y);
591 break;
592
593 case panel::WindowState::PRESSED:
594- for (GLTexture *icon : close_pressed_tex_)
595- DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height);
596+ DrawTexture(close_pressed_tex_, attrib, transform, mask, x, y);
597 break;
598 }
599
600- close_button_geo_.Set(x, y, max_height, max_width);
601+ close_button_geo_.Set(x, y, SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE);
602 }
603 else if (!close_button_geo_.IsNull())
604 {
605@@ -3730,14 +3761,27 @@
606 }
607 }
608
609+nux::Geometry UnityWindow::GetScaledGeometry()
610+{
611+ ScaleWindow *scale_win = ScaleWindow::get(window);
612+
613+ ScalePosition const& pos = scale_win->getCurrentPosition();
614+ auto const& border_rect = window->borderRect();
615+ auto const& deco_ext = window->border();
616+
617+ const unsigned width = std::floor(border_rect.width() * pos.scale);
618+ const unsigned height = std::floor(border_rect.height() * pos.scale);
619+ const int x = pos.x() + window->x() - std::floor(deco_ext.left * pos.scale);
620+ const int y = pos.y() + window->y() - std::floor(deco_ext.top * pos.scale);
621+
622+ return nux::Geometry(x, y, width, height);
623+}
624+
625 void UnityWindow::OnInitiateSpreed()
626 {
627- auto const& windows = screen->windows();
628- if (std::find(windows.begin(), windows.end(), window) == windows.end())
629- return;
630-
631 close_icon_state_ = panel::WindowState::NORMAL;
632- SetupScaleHeaderStyle();
633+ middle_clicked_ = false;
634+ SetupSharedTextures();
635
636 WindowManager *wm = WindowManager::Default();
637 Window xid = window->id();
638@@ -3748,10 +3792,6 @@
639
640 void UnityWindow::OnTerminateSpreed()
641 {
642- auto const& windows = screen->windows();
643- if (std::find(windows.begin(), windows.end(), window) == windows.end())
644- return;
645-
646 WindowManager *wm = WindowManager::Default();
647 Window xid = window->id();
648
649@@ -3813,6 +3853,33 @@
650 return true;
651 }
652
653+namespace debug
654+{
655+
656+ScreenIntrospection::ScreenIntrospection(CompScreen* screen)
657+ : screen_(screen)
658+{}
659+
660+std::string ScreenIntrospection::GetName() const
661+{
662+ return "Screen";
663+}
664+
665+void ScreenIntrospection::AddProperties(GVariantBuilder* builder)
666+{}
667+
668+Introspectable::IntrospectableList ScreenIntrospection::GetIntrospectableChildren()
669+{
670+ IntrospectableList children;
671+
672+ for (auto const& win : screen_->windows())
673+ children.push_back(UnityWindow::get(win));
674+
675+ return children;
676+}
677+
678+} // debug namespace
679+
680 namespace
681 {
682
683
684=== modified file 'plugins/unityshell/src/unityshell.h'
685--- plugins/unityshell/src/unityshell.h 2012-09-19 16:57:39 +0000
686+++ plugins/unityshell/src/unityshell.h 2012-09-24 16:14:49 +0000
687@@ -27,7 +27,6 @@
688 #include <Nux/WindowThread.h>
689 #include <NuxCore/Property.h>
690 #include <sigc++/sigc++.h>
691-#include <boost/shared_ptr.hpp>
692
693 #include <scale/scale.h>
694 #include <core/core.h>
695@@ -50,6 +49,7 @@
696 #include "PanelStyle.h"
697 #include "UScreen.h"
698 #include "DebugDBusInterface.h"
699+#include "ScreenIntrospection.h"
700 #include "SwitcherController.h"
701 #include "UBusWrapper.h"
702 #include "UnityshellPrivate.h"
703@@ -69,7 +69,7 @@
704
705 /* base screen class */
706 class UnityScreen :
707- public unity::debug::Introspectable,
708+ public debug::Introspectable,
709 public sigc::trackable,
710 public ScreenInterface,
711 public CompositeScreenInterface,
712@@ -307,7 +307,7 @@
713
714 nux::Property<nux::Geometry> primary_monitor_;
715
716- BGHash* _bghash;
717+ std::unique_ptr<BGHash> _bghash;
718
719 ::GLFramebufferObject *oldFbo;
720
721@@ -329,11 +329,13 @@
722 nux::ObjectPtr<nux::IOpenGLBaseTexture> panel_texture_;
723
724 bool scale_just_activated_;
725+ WindowMinimizeSpeedController minimize_speed_controller_;
726+
727+ debug::ScreenIntrospection screen_introspection_;
728
729 UBusManager ubus_manager_;
730 glib::SourceManager sources_;
731
732- WindowMinimizeSpeedController* minimize_speed_controller;
733 friend class UnityWindow;
734 };
735
736@@ -345,6 +347,7 @@
737 public WrapableHandler<ScaleWindowInterface, 4>,
738 public BaseSwitchWindow,
739 public PluginClassHandler <UnityWindow, CompWindow>,
740+ public debug::Introspectable,
741 public sigc::trackable
742 {
743 public:
744@@ -391,6 +394,7 @@
745
746 bool place(CompPoint& pos);
747 CompPoint tryNotIntersectUI(CompPoint& pos);
748+ nux::Geometry GetScaledGeometry();
749
750 void paintThumbnail (nux::Geometry const& bounding, float alpha);
751
752@@ -413,6 +417,10 @@
753 const CompRegion &,
754 unsigned int);
755
756+protected:
757+ std::string GetName() const;
758+ void AddProperties(GVariantBuilder* builder);
759+
760 private:
761 struct CairoContext;
762
763@@ -449,23 +457,16 @@
764
765 compiz::WindowInputRemoverLock::Ptr GetInputRemover ();
766
767- void DrawWindowDecoration(GLWindowPaintAttrib const& attrib,
768- GLMatrix const& transform,
769- unsigned int mask,
770- bool highlighted,
771+ void DrawWindowDecoration(GLWindowPaintAttrib const& attrib, GLMatrix const& transform,
772+ unsigned int mask, bool highlighted,
773 int x, int y, unsigned width, unsigned height);
774- void DrawTexture(GLTexture *icon,
775- const GLWindowPaintAttrib& attrib,
776- const GLMatrix& transform,
777- unsigned int mask,
778- float x, float y,
779- int &maxWidth, int &maxHeight);
780- void RenderText(CairoContext const& context,
781- float x, float y,
782- float maxWidth, float maxHeight);
783+ void DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const& attrib,
784+ GLMatrix const& transform, unsigned int mask, int x, int y);
785+ void RenderText(CairoContext const& context, int x, int y, int width, int height);
786
787- void SetupScaleHeaderStyle();
788- void LoadCloseIcon(panel::WindowState state, GLTexture::List& texture);
789+ static void SetupSharedTextures();
790+ static void CleanupSharedTextures();
791+ static void LoadCloseIcon(panel::WindowState state, GLTexture::List& texture);
792
793 static GLTexture::List close_normal_tex_;
794 static GLTexture::List close_prelight_tex_;
795@@ -473,7 +474,10 @@
796 compiz::WindowInputRemoverLock::Weak input_remover_;
797 panel::WindowState close_icon_state_;
798 nux::Geometry close_button_geo_;
799+ bool middle_clicked_;
800 glib::Source::UniquePtr focus_desktop_timeout_;
801+
802+ friend class UnityScreen;
803 };
804
805
806
807=== added file 'tests/autopilot/unity/emulators/screen.py'
808--- tests/autopilot/unity/emulators/screen.py 1970-01-01 00:00:00 +0000
809+++ tests/autopilot/unity/emulators/screen.py 2012-09-24 16:14:49 +0000
810@@ -0,0 +1,42 @@
811+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
812+# Copyright 2012 Canonical
813+# Author: Marco Trevisan (Treviño)
814+#
815+# This program is free software: you can redistribute it and/or modify it
816+# under the terms of the GNU General Public License version 3, as published
817+# by the Free Software Foundation.
818+#
819+
820+from __future__ import absolute_import
821+
822+import logging
823+from unity.emulators import UnityIntrospectionObject
824+logger = logging.getLogger(__name__)
825+
826+
827+class Screen(UnityIntrospectionObject):
828+ """The Screen class."""
829+
830+ @property
831+ def windows(self):
832+ """Return the available windows, or None."""
833+ return self.get_children_by_type(Window)
834+
835+ @property
836+ def scaled_windows(self):
837+ """Return the available scaled windows, or None."""
838+ return self.get_children_by_type(Window, scaled=True)
839+
840+
841+class Window(UnityIntrospectionObject):
842+ """An individual window."""
843+
844+ @property
845+ def geometry(self):
846+ """Returns a tuple of (x,y,w,h) for the current window."""
847+ return (self.x, self.y, self.width, self.height)
848+
849+ @property
850+ def scale_close_geometry(self):
851+ """Returns a tuple of (x,y,w,h) for the scale close button."""
852+ return (self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height)
853
854=== modified file 'tests/autopilot/unity/tests/__init__.py'
855--- tests/autopilot/unity/tests/__init__.py 2012-07-12 20:54:59 +0000
856+++ tests/autopilot/unity/tests/__init__.py 2012-09-24 16:14:49 +0000
857@@ -20,6 +20,7 @@
858 from testtools.content import text_content
859 from testtools.matchers import Equals
860
861+from unity.emulators.screen import Screen
862 from unity.emulators.dash import Dash
863 from unity.emulators.hud import Hud
864 from unity.emulators.launcher import LauncherController
865@@ -114,6 +115,12 @@
866 return True
867
868 @property
869+ def screen(self):
870+ if not getattr(self, '__screen', None):
871+ self.__screen = self._get_screen()
872+ return self.__screen
873+
874+ @property
875 def dash(self):
876 if not getattr(self, '__dash', None):
877 self.__dash = Dash()
878@@ -155,6 +162,11 @@
879 self.__workspace = WorkspaceManager()
880 return self.__workspace
881
882+ def _get_screen(self):
883+ screens = Screen.get_all_instances()
884+ self.assertThat(len(screens), Equals(1))
885+ return screens[0]
886+
887 def _get_launcher_controller(self):
888 controllers = LauncherController.get_all_instances()
889 self.assertThat(len(controllers), Equals(1))
890
891=== added file 'tests/autopilot/unity/tests/test_spread.py'
892--- tests/autopilot/unity/tests/test_spread.py 1970-01-01 00:00:00 +0000
893+++ tests/autopilot/unity/tests/test_spread.py 2012-09-24 16:14:49 +0000
894@@ -0,0 +1,124 @@
895+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
896+# Copyright 2012 Canonical
897+# Author: Marco Trevisan (Treviño)
898+#
899+# This program is free software: you can redistribute it and/or modify it
900+# under the terms of the GNU General Public License version 3, as published
901+# by the Free Software Foundation.
902+
903+from __future__ import absolute_import
904+
905+from autopilot.matchers import Eventually
906+import logging
907+from time import sleep
908+from testtools.matchers import Equals, NotEquals
909+
910+from unity.tests import UnityTestCase
911+from unity.emulators.screen import Screen
912+
913+
914+class SpreadTests(UnityTestCase):
915+ """Spread tests"""
916+
917+ def start_test_application_windows(self, app_name, num_windows=2):
918+ """Start a given number of windows of the requested application"""
919+ self.close_all_app(app_name)
920+ windows = []
921+
922+ for i in range(num_windows):
923+ win = self.start_app_window(app_name)
924+ if len(windows):
925+ self.assertThat(win.application, Equals(windows[-1].application))
926+
927+ windows.append(win)
928+
929+ self.assertThat(len(windows), Equals(num_windows))
930+
931+ return windows
932+
933+ def initiate_spread_for_screen(self):
934+ """Initiate the Spread for all windows"""
935+ self.addCleanup(self.keybinding, "spread/cancel")
936+ self.keybinding("spread/start")
937+ sleep(1)
938+ self.assertThat(self.window_manager.scale_active, Eventually(Equals(True)))
939+
940+
941+ def initiate_spread_for_application(self, desktop_id):
942+ """Initiate the Spread for windows of the given app"""
943+ icon = self.launcher.model.get_icon(desktop_id=desktop_id)
944+ self.assertThat(lambda: icon, Eventually(NotEquals(None)))
945+ launcher = self.launcher.get_launcher_for_monitor(self.screen_geo.get_primary_monitor())
946+
947+ self.addCleanup(self.keybinding, "spread/cancel")
948+ launcher.click_launcher_icon(icon)
949+ self.assertThat(self.window_manager.scale_active_for_group, Eventually(Equals(True)))
950+
951+ def assertWindowIsNotScaled(self, xid):
952+ """Assert that a window is not scaled"""
953+ refresh_fn = lambda: xid in [w.xid for w in self.screen.scaled_windows]
954+ self.assertThat(refresh_fn, Eventually(Equals(False)))
955+
956+ def assertWindowIsClosed(self, xid):
957+ """Assert that a window is not in the list of the open windows"""
958+ refresh_fn = lambda: xid in [w.x_id for w in self.bamf.get_open_windows()]
959+ self.assertThat(refresh_fn, Eventually(Equals(False)))
960+
961+
962+ def test_scale_application_windows(self):
963+ """Test if all the windows of an application are scaled when application spread is initiated"""
964+ [win1, win2] = self.start_test_application_windows("Calculator")
965+ self.initiate_spread_for_application(win1.application.desktop_file)
966+
967+ self.assertThat(lambda: len(self.screen.scaled_windows), Eventually(Equals(2)))
968+ self.assertThat(lambda: (win1.x_id and win2.x_id) in [w.xid for w in self.screen.scaled_windows],
969+ Eventually(Equals(True)))
970+
971+ def test_scaled_window_is_focused_on_click(self):
972+ """Test that a window is focused when clicked in spread"""
973+ windows = self.start_test_application_windows("Calculator", 3)
974+ self.initiate_spread_for_application(windows[0].application.desktop_file)
975+
976+ not_focused = [w for w in windows if not w.is_focused][0]
977+
978+ target_xid = not_focused.x_id
979+ [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid]
980+
981+ (x, y, w, h) = target_win.geometry
982+ self.mouse.move(x + w / 2, y + h / 2)
983+ sleep(.5)
984+ self.mouse.click()
985+
986+ self.assertThat(lambda: not_focused.is_focused, Eventually(Equals(True)))
987+
988+ def test_scaled_window_closes_on_middle_click(self):
989+ """Test that a window is closed when middle-clicked in spread"""
990+ win = self.start_test_application_windows("Calculator", 2)[0]
991+ self.initiate_spread_for_application(win.application.desktop_file)
992+
993+ target_xid = win.x_id
994+ [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid]
995+
996+ (x, y, w, h) = target_win.geometry
997+ self.mouse.move(x + w / 2, y + h / 2)
998+ sleep(.5)
999+ self.mouse.click(button=2)
1000+
1001+ self.assertWindowIsNotScaled(target_xid)
1002+ self.assertWindowIsClosed(target_xid)
1003+
1004+ def test_scaled_window_closes_on_close_button_click(self):
1005+ """Test that a window is closed when its close button is clicked in spread"""
1006+ win = self.start_test_application_windows("Calculator", 1)[0]
1007+ self.initiate_spread_for_screen()
1008+
1009+ target_xid = win.x_id
1010+ [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid]
1011+
1012+ (x, y, w, h) = target_win.scale_close_geometry
1013+ self.mouse.move(x + w / 2, y + h / 2)
1014+ sleep(.5)
1015+ self.mouse.click()
1016+
1017+ self.assertWindowIsNotScaled(target_xid)
1018+ self.assertWindowIsClosed(target_xid)