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

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Brandon Schaefer
Approved revision: no longer in the source branch.
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) Approve
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.
Revision history for this message
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
=== added file 'plugins/unityshell/src/ScreenIntrospection.h'
--- plugins/unityshell/src/ScreenIntrospection.h 1970-01-01 00:00:00 +0000
+++ plugins/unityshell/src/ScreenIntrospection.h 2012-09-24 16:14:49 +0000
@@ -0,0 +1,48 @@
1// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2/*
3 * Copyright (C) 2012 Canonical Ltd
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com>
18 */
19
20#ifndef SCREEN_INTROSPECTION_H
21#define SCREEN_INTROSPECTION_H
22
23#include "Introspectable.h"
24#include <core/core.h>
25
26namespace unity
27{
28namespace debug
29{
30
31class ScreenIntrospection : public Introspectable
32{
33public:
34 ScreenIntrospection(CompScreen* screen);
35
36protected:
37 std::string GetName() const;
38 void AddProperties(GVariantBuilder* builder);
39 IntrospectableList GetIntrospectableChildren();
40
41private:
42 CompScreen* screen_;
43};
44
45}
46}
47
48#endif
049
=== modified file 'plugins/unityshell/src/unityshell.cpp'
--- plugins/unityshell/src/unityshell.cpp 2012-09-21 21:52:27 +0000
+++ plugins/unityshell/src/unityshell.cpp 2012-09-24 16:14:49 +0000
@@ -26,6 +26,8 @@
26#include <Nux/WindowCompositor.h>26#include <Nux/WindowCompositor.h>
27#include <Nux/NuxTimerTickSource.h>27#include <Nux/NuxTimerTickSource.h>
2828
29#include <UnityCore/Variant.h>
30
29#include "BaseWindowRaiserImp.h"31#include "BaseWindowRaiserImp.h"
30#include "IconRenderer.h"32#include "IconRenderer.h"
31#include "Launcher.h"33#include "Launcher.h"
@@ -125,7 +127,6 @@
125 , allowWindowPaint(false)127 , allowWindowPaint(false)
126 , _key_nav_mode_requested(false)128 , _key_nav_mode_requested(false)
127 , _last_output(nullptr)129 , _last_output(nullptr)
128 , _bghash(NULL)
129 , grab_index_ (0)130 , grab_index_ (0)
130 , painting_tray_ (false)131 , painting_tray_ (false)
131 , last_scroll_event_(0)132 , last_scroll_event_(0)
@@ -133,7 +134,7 @@
133 , panel_texture_has_changed_(true)134 , panel_texture_has_changed_(true)
134 , paint_panel_(false)135 , paint_panel_(false)
135 , scale_just_activated_(false)136 , scale_just_activated_(false)
136 , minimize_speed_controller(new WindowMinimizeSpeedController())137 , screen_introspection_(screen)
137{138{
138 Timer timer;139 Timer timer;
139#ifndef USE_GLES140#ifndef USE_GLES
@@ -244,7 +245,7 @@
244 // _bghash is a pointer. We don't want it to be created before Nux system has had a chance245 // _bghash is a pointer. We don't want it to be created before Nux system has had a chance
245 // to start. BGHash relies on animations. Nux animation system starts after the WindowThread246 // to start. BGHash relies on animations. Nux animation system starts after the WindowThread
246 // has been created.247 // has been created.
247 _bghash = new BGHash();248 _bghash.reset(new BGHash());
248249
249 unity_a11y_init(wt.get());250 unity_a11y_init(wt.get());
250251
@@ -375,13 +376,15 @@
375 Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());;376 Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());;
376 XSelectInput(display, GDK_ROOT_WINDOW(), PropertyChangeMask);377 XSelectInput(display, GDK_ROOT_WINDOW(), PropertyChangeMask);
377 LOG_INFO(logger) << "UnityScreen constructed: " << timer.ElapsedSeconds() << "s";378 LOG_INFO(logger) << "UnityScreen constructed: " << timer.ElapsedSeconds() << "s";
379
380 panel::Style::Instance().changed.connect(sigc::mem_fun(this, &UnityScreen::OnPanelStyleChanged));
381
382 minimize_speed_controller_.DurationChanged.connect(
383 sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged)
384 );
385
386 AddChild(&screen_introspection_);
378 }387 }
379
380 panel::Style::Instance().changed.connect(sigc::mem_fun(this, &UnityScreen::OnPanelStyleChanged));
381
382 minimize_speed_controller->DurationChanged.connect(
383 sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged)
384 );
385}388}
386389
387UnityScreen::~UnityScreen()390UnityScreen::~UnityScreen()
@@ -391,7 +394,6 @@
391 unity_a11y_finalize();394 unity_a11y_finalize();
392 ::unity::ui::IconRenderer::DestroyTextures();395 ::unity::ui::IconRenderer::DestroyTextures();
393 QuicklistManager::Destroy();396 QuicklistManager::Destroy();
394 delete _bghash;
395397
396 reset_glib_logging();398 reset_glib_logging();
397}399}
@@ -659,10 +661,15 @@
659 wy = y + (last_bound.height - height) / 2;661 wy = y + (last_bound.height - height) / 2;
660}662}
661663
662void664void UnityScreen::OnPanelStyleChanged()
663UnityScreen::OnPanelStyleChanged()
664{665{
665 panel_texture_has_changed_ = true;666 panel_texture_has_changed_ = true;
667
668 // Reload the windows themed textures
669 UnityWindow::CleanupSharedTextures();
670
671 if (WindowManager::Default()->IsScaleActive())
672 UnityWindow::SetupSharedTextures();
666}673}
667674
668void UnityScreen::paintDisplay()675void UnityScreen::paintDisplay()
@@ -1136,6 +1143,12 @@
1136 DoAddDamage();1143 DoAddDamage();
1137 handled = true;1144 handled = true;
1138 }1145 }
1146 else if (event->xbutton.button == Button2 &&
1147 GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))
1148 {
1149 middle_clicked_ = true;
1150 handled = true;
1151 }
1139 break;1152 break;
11401153
1141 case ButtonRelease:1154 case ButtonRelease:
@@ -1155,6 +1168,18 @@
11551168
1156 handled = true;1169 handled = true;
1157 }1170 }
1171
1172 if (middle_clicked_)
1173 {
1174 if (event->xbutton.button == Button2 &&
1175 GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))
1176 {
1177 window->close(0);
1178 }
1179
1180 middle_clicked_ = false;
1181 handled = true;
1182 }
1158 }1183 }
1159 break;1184 break;
11601185
@@ -2277,8 +2302,7 @@
2277}2302}
22782303
2279void UnityScreen::AddProperties(GVariantBuilder* builder)2304void UnityScreen::AddProperties(GVariantBuilder* builder)
2280{2305{}
2281}
22822306
2283std::string UnityScreen::GetName() const2307std::string UnityScreen::GetName() const
2284{2308{
@@ -2469,10 +2493,10 @@
2469 CompOption::Value& value = o.value();2493 CompOption::Value& value = o.value();
2470 CompOption::Value::Vector& list = value.list();2494 CompOption::Value::Vector& list = value.list();
2471 CompOption::Value::Vector::iterator i = list.begin();2495 CompOption::Value::Vector::iterator i = list.begin();
2472 if (i != list.end()) {2496 if (i != list.end())
2473 i->set(minimize_speed_controller->getDuration());2497 i->set(minimize_speed_controller_.getDuration());
2474 }2498
2475 value.set(list); 2499 value.set(list);
2476 screen->setOptionForPlugin(p->vTable->name().c_str(),2500 screen->setOptionForPlugin(p->vTable->name().c_str(),
2477 o.name().c_str(), value);2501 o.name().c_str(), value);
2478 break;2502 break;
@@ -2603,7 +2627,7 @@
2603 case CompWindowNotifyMinimize:2627 case CompWindowNotifyMinimize:
2604 /* Updating the count in dconf will trigger a "changed" signal to which2628 /* Updating the count in dconf will trigger a "changed" signal to which
2605 * the method setting the new animation speed is attached */2629 * the method setting the new animation speed is attached */
2606 UnityScreen::get(screen)->minimize_speed_controller->UpdateCount();2630 UnityScreen::get(screen)->minimize_speed_controller_.UpdateCount();
2607 break;2631 break;
2608 default:2632 default:
2609 break;2633 break;
@@ -3458,107 +3482,117 @@
3458 WindowManager::Default()->terminate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnTerminateSpreed));3482 WindowManager::Default()->terminate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnTerminateSpreed));
3459}3483}
34603484
3461void3485void UnityWindow::AddProperties(GVariantBuilder* builder)
3462UnityWindow::DrawTexture(GLTexture* icon,3486{
3463 const GLWindowPaintAttrib& attrib,3487 Window xid = window->id();
3464 const GLMatrix& transform,3488 auto const& swins = ScaleScreen::get(screen)->getWindows();
3465 unsigned int mask,3489 bool scaled = std::find(swins.begin(), swins.end(), ScaleWindow::get(window)) != swins.end();
3466 float x, float y,3490 auto wm = WindowManager::Default();
3467 int &maxWidth, int &maxHeight)3491
3468{3492 variant::BuilderWrapper(builder)
3469 if (icon)3493 .add(scaled ? GetScaledGeometry() : wm->GetWindowGeometry(xid))
3494 .add("xid", xid)
3495 .add("title", wm->GetWindowName(xid))
3496 .add("scaled", scaled)
3497 .add("scaled_close_x", close_button_geo_.x)
3498 .add("scaled_close_y", close_button_geo_.y)
3499 .add("scaled_close_width", close_button_geo_.width)
3500 .add("scaled_close_height", close_button_geo_.height);
3501}
3502
3503std::string UnityWindow::GetName() const
3504{
3505 return "Window";
3506}
3507
3508void UnityWindow::DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const& attrib,
3509 GLMatrix const& transform, unsigned int mask, int x, int y)
3510{
3511 for (auto const& texture : textures)
3470 {3512 {
3471 int width, height;3513 if (!texture)
3472 width = icon->width();3514 continue;
3473 height = icon->height();3515
3474
3475 if (height > maxHeight)
3476 maxHeight = height;
3477
3478 if (width > maxWidth)
3479 maxWidth = width;
3480
3481 CompRegion iconReg(0, 0, width, height);
3482 GLTexture::MatrixList ml(1);
3483
3484 ml[0] = icon->matrix();
3485 gWindow->vertexBuffer()->begin();3516 gWindow->vertexBuffer()->begin();
3486 if (width && height)3517
3487 gWindow->glAddGeometry(ml, iconReg, iconReg);3518 if (texture->width() && texture->height())
3519 {
3520 GLTexture::MatrixList ml(1);
3521 ml[0] = texture->matrix();
3522 CompRegion texture_region(0, 0, texture->width(), texture->height());
3523 gWindow->glAddGeometry(ml, texture_region, texture_region);
3524 }
34883525
3489 if (gWindow->vertexBuffer()->end())3526 if (gWindow->vertexBuffer()->end())
3490 {3527 {
3491 GLMatrix wTransform(transform);3528 GLMatrix wTransform(transform);
3492
3493 wTransform.translate(x, y, 0.0f);3529 wTransform.translate(x, y, 0.0f);
34943530
3495 gWindow->glDrawTexture(icon, wTransform, attrib, mask);3531 gWindow->glDrawTexture(texture, wTransform, attrib, mask);
3496 }3532 }
3497 }3533 }
3498}3534}
34993535
3500void3536void UnityWindow::RenderText(CairoContext const& context, int x, int y, int width, int height)
3501UnityWindow::RenderText(UnityWindow::CairoContext const& context,
3502 float x, float y,
3503 float maxWidth, float maxHeight)
3504{3537{
3505 panel::Style& style = panel::Style::Instance();3538 panel::Style& style = panel::Style::Instance();
3506 std::string fontDescription(style.GetFontDescription(panel::PanelItem::TITLE));3539 std::string const& font_desc = style.GetFontDescription(panel::PanelItem::TITLE);
35073540
3508 glib::Object<PangoLayout> layout(pango_cairo_create_layout(context.cr_));3541 glib::Object<PangoLayout> layout(pango_cairo_create_layout(context.cr_));
3509 std::shared_ptr<PangoFontDescription> font(pango_font_description_from_string(fontDescription.c_str()),3542 std::shared_ptr<PangoFontDescription> font(pango_font_description_from_string(font_desc.c_str()),
3510 pango_font_description_free);3543 pango_font_description_free);
35113544
3512 pango_layout_set_font_description(layout, font.get());3545 pango_layout_set_font_description(layout, font.get());
35133546
3514 GdkScreen* gdkScreen = gdk_screen_get_default();3547 GdkScreen* gdk_screen = gdk_screen_get_default();
3515 PangoContext* pCxt = pango_layout_get_context(layout);3548 PangoContext* pango_ctx = pango_layout_get_context(layout);
3516 int dpi = style.GetTextDPI();3549 int dpi = style.GetTextDPI();
35173550
3518 pango_cairo_context_set_font_options(pCxt, gdk_screen_get_font_options(gdkScreen));3551 pango_cairo_context_set_font_options(pango_ctx, gdk_screen_get_font_options(gdk_screen));
3519 pango_cairo_context_set_resolution(pCxt, dpi / static_cast<float>(PANGO_SCALE));3552 pango_cairo_context_set_resolution(pango_ctx, dpi / static_cast<float>(PANGO_SCALE));
3520 pango_layout_context_changed(layout);3553 pango_layout_context_changed(layout);
35213554
3522 pango_layout_set_height(layout, maxHeight);3555 std::string const& win_title = WindowManager::Default()->GetWindowName(window->id());
3556 pango_layout_set_height(layout, height);
3523 pango_layout_set_width(layout, -1); //avoid wrap lines3557 pango_layout_set_width(layout, -1); //avoid wrap lines
3524 pango_layout_set_auto_dir(layout, false);3558 pango_layout_set_auto_dir(layout, false);
3525 pango_layout_set_text(layout,3559 pango_layout_set_text(layout, win_title.c_str(), -1);
3526 WindowManager::Default()->GetWindowName(window->id()).c_str(),
3527 -1);
35283560
3529 /* update the size of the pango layout */3561 /* update the size of the pango layout */
3530 pango_cairo_update_layout(context.cr_, layout);3562 pango_cairo_update_layout(context.cr_, layout);
3531 cairo_set_operator(context.cr_, CAIRO_OPERATOR_OVER);3563
3532 cairo_set_source_rgba(context.cr_,3564 GtkStyleContext* style_context = style.GetStyleContext();
3533 1.0,3565 gtk_style_context_save(style_context);
3534 1.0,3566
3535 1.0,3567 std::shared_ptr<GtkWidgetPath> widget_path(gtk_widget_path_new(), gtk_widget_path_free);
3536 1.0);3568 gtk_widget_path_append_type(widget_path.get(), GTK_TYPE_MENU_BAR);
3569 gtk_widget_path_append_type(widget_path.get(), GTK_TYPE_MENU_ITEM);
3570 gtk_widget_path_iter_set_name(widget_path.get(), -1 , "UnityPanelWidget");
3571
3572 gtk_style_context_set_path(style_context, widget_path.get());
3573 gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_MENUBAR);
3574 gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_MENUITEM);
35373575
3538 // alignment3576 // alignment
3539 PangoRectangle lRect;3577 PangoRectangle lRect;
3540 int textWidth, textHeight;3578 pango_layout_get_extents(layout, nullptr, &lRect);
35413579 int text_width = lRect.width / PANGO_SCALE;
3542 pango_layout_get_extents(layout, NULL, &lRect);3580 int text_height = lRect.height / PANGO_SCALE;
3543 textWidth = lRect.width / PANGO_SCALE;3581 y += (height - text_height) / 2.0f;
3544 textHeight = lRect.height / PANGO_SCALE;3582 int text_space = width - x;
35453583
3546 y = ((maxHeight - textHeight) / 2.0) + y;3584 if (text_width > text_space)
3547 cairo_translate(context.cr_, x, y);
3548
3549 if (textWidth > maxWidth)
3550 {3585 {
3551 // apply a fade effect in the right corner3586 // Cut the text with fade
3552 const int outPixels = textWidth - maxWidth;3587 int out_pixels = text_width - text_space;
3553 const int fadingPixels = 35;3588 const int fading_pixels = 35;
3554 const int fadingWidth = outPixels < fadingPixels ? outPixels : fadingPixels;3589 int fading_width = (out_pixels < fading_pixels) ? out_pixels : fading_pixels;
35553590
3556 cairo_push_group(context.cr_);3591 cairo_push_group(context.cr_);
3557 pango_cairo_show_layout(context.cr_, layout);3592 gtk_render_layout(style_context, context.cr_, x, y, layout);
3558 cairo_pop_group_to_source(context.cr_);3593 cairo_pop_group_to_source(context.cr_);
35593594
3560 std::shared_ptr<cairo_pattern_t> linpat(cairo_pattern_create_linear(maxWidth - fadingWidth,3595 std::shared_ptr<cairo_pattern_t> linpat(cairo_pattern_create_linear(width - fading_width, y, width, y),
3561 y, maxWidth, y),
3562 cairo_pattern_destroy);3596 cairo_pattern_destroy);
3563 cairo_pattern_add_color_stop_rgba(linpat.get(), 0, 0, 0, 0, 1);3597 cairo_pattern_add_color_stop_rgba(linpat.get(), 0, 0, 0, 0, 1);
3564 cairo_pattern_add_color_stop_rgba(linpat.get(), 1, 0, 0, 0, 0);3598 cairo_pattern_add_color_stop_rgba(linpat.get(), 1, 0, 0, 0, 0);
@@ -3566,61 +3600,55 @@
3566 }3600 }
3567 else3601 else
3568 {3602 {
3569 pango_cairo_show_layout(context.cr_, layout);3603 gtk_render_layout(style_context, context.cr_, x, y, layout);
3570 }3604 }
3605
3606 gtk_style_context_restore(style_context);
3571}3607}
35723608
3573void3609void UnityWindow::DrawWindowDecoration(GLWindowPaintAttrib const& attrib,
3574UnityWindow::DrawWindowDecoration(GLWindowPaintAttrib const& attrib,3610 GLMatrix const& transform,
3575 GLMatrix const& transform,3611 unsigned int mask,
3576 unsigned int mask,3612 bool highlighted,
3577 bool highlighted,3613 int x, int y, unsigned width, unsigned height)
3578 int x, int y, unsigned width, unsigned height)
3579{3614{
3580 // Paint a fake window decoration3615 // Paint a fake window decoration
3581 CairoContext context(width, height);3616 CairoContext context(width, height);
35823617
3583 cairo_save(context.cr_);3618 cairo_save(context.cr_);
3619
3620 // Draw window decoration based on gtk style
3584 cairo_push_group(context.cr_);3621 cairo_push_group(context.cr_);
3622 auto& style = panel::Style::Instance();
3623 gtk_render_background(style.GetStyleContext(), context.cr_, 0, 0, width, height);
3624 gtk_render_frame(style.GetStyleContext(), context.cr_, 0, 0, width, height);
3625 cairo_pop_group_to_source(context.cr_);
35853626
3586 // Round window decoration top border3627 // Round window decoration top border
3587 const double aspect = 1.0;3628 const double aspect = ScaleWindow::get(window)->getCurrentPosition().scale;
3588 const double corner_radius = height / 10.0;3629 const double radius = 8.0 * aspect;
3589 const double radius = corner_radius / aspect;
3590 const double degrees = M_PI / 180.0;
35913630
3592 cairo_new_sub_path(context.cr_);3631 cairo_new_sub_path(context.cr_);
35933632 cairo_line_to(context.cr_, 0, height);
3594 cairo_arc(context.cr_, radius, radius, radius, 180 * degrees, 270 * degrees);3633 cairo_arc(context.cr_, radius, radius, radius, M_PI, -M_PI * 0.5f);
3595 cairo_arc(context.cr_, width - radius, radius, radius, -90 * degrees, 0 * degrees);3634 cairo_line_to(context.cr_, width - radius, 0);
3635 cairo_arc(context.cr_, width - radius, radius, radius, M_PI * 0.5f, 0);
3596 cairo_line_to(context.cr_, width, height);3636 cairo_line_to(context.cr_, width, height);
3597 cairo_line_to(context.cr_, 0, height);
3598
3599 cairo_close_path(context.cr_);3637 cairo_close_path(context.cr_);
3600 cairo_clip(context.cr_);3638
36013639 cairo_fill(context.cr_);
3602 // Draw window decoration based on gtk style3640
3603 auto& style = panel::Style::Instance();
3604 gtk_render_background(style.GetStyleContext(), context.cr_, 0, 0, width, height);
3605 gtk_render_frame(style.GetStyleContext(), context.cr_, 0, 0, width, height);
3606
3607 cairo_pop_group_to_source(context.cr_);
3608
3609 cairo_paint_with_alpha(context.cr_, 1.0);
3610 cairo_restore(context.cr_);3641 cairo_restore(context.cr_);
36113642
3612 if (highlighted)3643 if (highlighted)
3613 {3644 {
3614 // Draw windows title3645 // Draw windows title
3615 const float xText = SCALE_ITEMS_PADDING * 2 + SCALE_CLOSE_ICON_SIZE;3646 const float xText = SCALE_ITEMS_PADDING * 2 + SCALE_CLOSE_ICON_SIZE;
3616 RenderText(context, xText, 0.0, width - xText - SCALE_ITEMS_PADDING, height);3647 RenderText(context, xText, 0.0, width - SCALE_ITEMS_PADDING, height);
3617 }3648 }
36183649
3619 mask |= PAINT_WINDOW_BLEND_MASK;3650 mask |= PAINT_WINDOW_BLEND_MASK;
3620 int maxWidth, maxHeight;3651 DrawTexture(context.texture_, attrib, transform, mask, x, y);
3621
3622 for (GLTexture *icon : context.texture_)
3623 DrawTexture(icon, attrib, transform, mask, x, y, maxWidth , maxHeight);
3624}3652}
36253653
3626void UnityWindow::LoadCloseIcon(panel::WindowState state, GLTexture::List& texture)3654void UnityWindow::LoadCloseIcon(panel::WindowState state, GLTexture::List& texture)
@@ -3631,12 +3659,12 @@
3631 auto& style = panel::Style::Instance();3659 auto& style = panel::Style::Instance();
3632 auto const& files = style.GetWindowButtonFileNames(panel::WindowButtonType::CLOSE, state);3660 auto const& files = style.GetWindowButtonFileNames(panel::WindowButtonType::CLOSE, state);
36333661
3634 CompString pName("unityshell");3662 CompString plugin("unityshell");
3635 for (std::string const& file : files)3663 for (std::string const& file : files)
3636 {3664 {
3637 CompString fileName(file.c_str());3665 CompString file_name = file;
3638 CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE);3666 CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE);
3639 texture = GLTexture::readImageToTexture(fileName, pName, size);3667 texture = GLTexture::readImageToTexture(file_name, plugin, size);
3640 if (!texture.empty())3668 if (!texture.empty())
3641 break;3669 break;
3642 }3670 }
@@ -3649,49 +3677,57 @@
3649 else if (state == panel::WindowState::PRESSED)3677 else if (state == panel::WindowState::PRESSED)
3650 suffix = "_pressed";3678 suffix = "_pressed";
36513679
3652 CompString fileName((PKGDATADIR"/close_dash" + suffix + ".png").c_str());3680 CompString file_name(PKGDATADIR"/close_dash" + suffix + ".png");
3653 CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE);3681 CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE);
3654 texture = GLTexture::readImageToTexture(fileName, pName, size);3682 texture = GLTexture::readImageToTexture(file_name, plugin, size);
3655 }3683 }
3656}3684}
36573685
3658void UnityWindow::SetupScaleHeaderStyle()3686void UnityWindow::SetupSharedTextures()
3659{3687{
3660 LoadCloseIcon(panel::WindowState::NORMAL, close_normal_tex_);3688 LoadCloseIcon(panel::WindowState::NORMAL, close_normal_tex_);
3661 LoadCloseIcon(panel::WindowState::PRELIGHT, close_prelight_tex_);3689 LoadCloseIcon(panel::WindowState::PRELIGHT, close_prelight_tex_);
3662 LoadCloseIcon(panel::WindowState::PRESSED, close_pressed_tex_);3690 LoadCloseIcon(panel::WindowState::PRESSED, close_pressed_tex_);
3663}3691}
36643692
3693void UnityWindow::CleanupSharedTextures()
3694{
3695 close_normal_tex_.clear();
3696 close_prelight_tex_.clear();
3697 close_pressed_tex_.clear();
3698}
3699
3665void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib,3700void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib,
3666 GLMatrix const& transform,3701 GLMatrix const& transform,
3667 CompRegion const& region,3702 CompRegion const& region,
3668 unsigned int mask)3703 unsigned int mask)
3669{3704{
3670 ScaleWindow *scale_win = ScaleWindow::get(window);3705 ScaleWindow *scale_win = ScaleWindow::get(window);
3671 if (!scale_win)
3672 return;
3673
3674 scale_win->scalePaintDecoration(attrib, transform, region, mask);3706 scale_win->scalePaintDecoration(attrib, transform, region, mask);
36753707
3676 if (!scale_win->hasSlot()) // animation not finished3708 if (!scale_win->hasSlot()) // animation not finished
3677 return;3709 return;
36783710
3679 ScaleScreen* ss = ScaleScreen::get(screen);3711 ScaleScreen* ss = ScaleScreen::get(screen);
3712 auto state = ss->getState();
3713
3714 if (state != ScaleScreen::Wait && state != ScaleScreen::Out)
3715 return;
3716
3717 auto const& scaled_geo = GetScaledGeometry();
3718 auto const& decoration_extents = window->border();
3719 auto const& pos = scale_win->getCurrentPosition();
3720
3680 const bool highlighted = (ss->getSelectedWindow() == window->id());3721 const bool highlighted = (ss->getSelectedWindow() == window->id());
36813722 int width = scaled_geo.width;
3682 ScalePosition const& pos = scale_win->getCurrentPosition();3723 int height = decoration_extents.top;
3683 auto const& border_rect = window->borderRect();3724 int x = scaled_geo.x;
3684 auto const& deco_ext = window->border();3725 int y = scaled_geo.y;
36853726
3686 const unsigned decoration_height = deco_ext.top;3727
3687 unsigned width = (border_rect.width() + deco_ext.left + deco_ext.right) * pos.scale;3728 // If window is not highlighted, we draw the decoration at scaled size
3688 unsigned height = decoration_height * pos.scale;3729 if (!highlighted)
3689 int x = pos.x() + border_rect.x();3730 height *= pos.scale;
3690 int y = pos.y() + border_rect.y() + decoration_height - height - 1;
3691
3692 // If window is highlighted, we draw the decoration at full size
3693 if (highlighted)
3694 height = decoration_height;
36953731
3696 DrawWindowDecoration(attrib, transform, mask, highlighted, x, y, width, height);3732 DrawWindowDecoration(attrib, transform, mask, highlighted, x, y, width, height);
36973733
@@ -3699,30 +3735,25 @@
3699 {3735 {
3700 x += SCALE_ITEMS_PADDING;3736 x += SCALE_ITEMS_PADDING;
3701 y += (height - SCALE_CLOSE_ICON_SIZE) / 2.0f;3737 y += (height - SCALE_CLOSE_ICON_SIZE) / 2.0f;
3702 int max_height = 0;
3703 int max_width = 0;
3704 mask |= PAINT_WINDOW_BLEND_MASK;3738 mask |= PAINT_WINDOW_BLEND_MASK;
37053739
3706 switch(close_icon_state_)3740 switch (close_icon_state_)
3707 {3741 {
3708 case panel::WindowState::NORMAL:3742 case panel::WindowState::NORMAL:
3709 default:3743 default:
3710 for (GLTexture *icon : close_normal_tex_)3744 DrawTexture(close_normal_tex_, attrib, transform, mask, x, y);
3711 DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height);
3712 break;3745 break;
37133746
3714 case panel::WindowState::PRELIGHT:3747 case panel::WindowState::PRELIGHT:
3715 for (GLTexture *icon : close_prelight_tex_)3748 DrawTexture(close_prelight_tex_, attrib, transform, mask, x, y);
3716 DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height);
3717 break;3749 break;
37183750
3719 case panel::WindowState::PRESSED:3751 case panel::WindowState::PRESSED:
3720 for (GLTexture *icon : close_pressed_tex_)3752 DrawTexture(close_pressed_tex_, attrib, transform, mask, x, y);
3721 DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height);
3722 break;3753 break;
3723 }3754 }
37243755
3725 close_button_geo_.Set(x, y, max_height, max_width);3756 close_button_geo_.Set(x, y, SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE);
3726 }3757 }
3727 else if (!close_button_geo_.IsNull())3758 else if (!close_button_geo_.IsNull())
3728 {3759 {
@@ -3730,14 +3761,27 @@
3730 }3761 }
3731}3762}
37323763
3764nux::Geometry UnityWindow::GetScaledGeometry()
3765{
3766 ScaleWindow *scale_win = ScaleWindow::get(window);
3767
3768 ScalePosition const& pos = scale_win->getCurrentPosition();
3769 auto const& border_rect = window->borderRect();
3770 auto const& deco_ext = window->border();
3771
3772 const unsigned width = std::floor(border_rect.width() * pos.scale);
3773 const unsigned height = std::floor(border_rect.height() * pos.scale);
3774 const int x = pos.x() + window->x() - std::floor(deco_ext.left * pos.scale);
3775 const int y = pos.y() + window->y() - std::floor(deco_ext.top * pos.scale);
3776
3777 return nux::Geometry(x, y, width, height);
3778}
3779
3733void UnityWindow::OnInitiateSpreed()3780void UnityWindow::OnInitiateSpreed()
3734{3781{
3735 auto const& windows = screen->windows();
3736 if (std::find(windows.begin(), windows.end(), window) == windows.end())
3737 return;
3738
3739 close_icon_state_ = panel::WindowState::NORMAL;3782 close_icon_state_ = panel::WindowState::NORMAL;
3740 SetupScaleHeaderStyle();3783 middle_clicked_ = false;
3784 SetupSharedTextures();
37413785
3742 WindowManager *wm = WindowManager::Default();3786 WindowManager *wm = WindowManager::Default();
3743 Window xid = window->id();3787 Window xid = window->id();
@@ -3748,10 +3792,6 @@
37483792
3749void UnityWindow::OnTerminateSpreed()3793void UnityWindow::OnTerminateSpreed()
3750{3794{
3751 auto const& windows = screen->windows();
3752 if (std::find(windows.begin(), windows.end(), window) == windows.end())
3753 return;
3754
3755 WindowManager *wm = WindowManager::Default();3795 WindowManager *wm = WindowManager::Default();
3756 Window xid = window->id();3796 Window xid = window->id();
37573797
@@ -3813,6 +3853,33 @@
3813 return true;3853 return true;
3814}3854}
38153855
3856namespace debug
3857{
3858
3859ScreenIntrospection::ScreenIntrospection(CompScreen* screen)
3860 : screen_(screen)
3861{}
3862
3863std::string ScreenIntrospection::GetName() const
3864{
3865 return "Screen";
3866}
3867
3868void ScreenIntrospection::AddProperties(GVariantBuilder* builder)
3869{}
3870
3871Introspectable::IntrospectableList ScreenIntrospection::GetIntrospectableChildren()
3872{
3873 IntrospectableList children;
3874
3875 for (auto const& win : screen_->windows())
3876 children.push_back(UnityWindow::get(win));
3877
3878 return children;
3879}
3880
3881} // debug namespace
3882
3816namespace3883namespace
3817{3884{
38183885
38193886
=== modified file 'plugins/unityshell/src/unityshell.h'
--- plugins/unityshell/src/unityshell.h 2012-09-19 16:57:39 +0000
+++ plugins/unityshell/src/unityshell.h 2012-09-24 16:14:49 +0000
@@ -27,7 +27,6 @@
27#include <Nux/WindowThread.h>27#include <Nux/WindowThread.h>
28#include <NuxCore/Property.h>28#include <NuxCore/Property.h>
29#include <sigc++/sigc++.h>29#include <sigc++/sigc++.h>
30#include <boost/shared_ptr.hpp>
3130
32#include <scale/scale.h>31#include <scale/scale.h>
33#include <core/core.h>32#include <core/core.h>
@@ -50,6 +49,7 @@
50#include "PanelStyle.h"49#include "PanelStyle.h"
51#include "UScreen.h"50#include "UScreen.h"
52#include "DebugDBusInterface.h"51#include "DebugDBusInterface.h"
52#include "ScreenIntrospection.h"
53#include "SwitcherController.h"53#include "SwitcherController.h"
54#include "UBusWrapper.h"54#include "UBusWrapper.h"
55#include "UnityshellPrivate.h"55#include "UnityshellPrivate.h"
@@ -69,7 +69,7 @@
6969
70/* base screen class */70/* base screen class */
71class UnityScreen :71class UnityScreen :
72 public unity::debug::Introspectable,72 public debug::Introspectable,
73 public sigc::trackable,73 public sigc::trackable,
74 public ScreenInterface,74 public ScreenInterface,
75 public CompositeScreenInterface,75 public CompositeScreenInterface,
@@ -307,7 +307,7 @@
307307
308 nux::Property<nux::Geometry> primary_monitor_;308 nux::Property<nux::Geometry> primary_monitor_;
309309
310 BGHash* _bghash;310 std::unique_ptr<BGHash> _bghash;
311311
312 ::GLFramebufferObject *oldFbo;312 ::GLFramebufferObject *oldFbo;
313313
@@ -329,11 +329,13 @@
329 nux::ObjectPtr<nux::IOpenGLBaseTexture> panel_texture_;329 nux::ObjectPtr<nux::IOpenGLBaseTexture> panel_texture_;
330330
331 bool scale_just_activated_;331 bool scale_just_activated_;
332 WindowMinimizeSpeedController minimize_speed_controller_;
333
334 debug::ScreenIntrospection screen_introspection_;
332335
333 UBusManager ubus_manager_;336 UBusManager ubus_manager_;
334 glib::SourceManager sources_;337 glib::SourceManager sources_;
335338
336 WindowMinimizeSpeedController* minimize_speed_controller;
337 friend class UnityWindow;339 friend class UnityWindow;
338};340};
339341
@@ -345,6 +347,7 @@
345 public WrapableHandler<ScaleWindowInterface, 4>,347 public WrapableHandler<ScaleWindowInterface, 4>,
346 public BaseSwitchWindow,348 public BaseSwitchWindow,
347 public PluginClassHandler <UnityWindow, CompWindow>,349 public PluginClassHandler <UnityWindow, CompWindow>,
350 public debug::Introspectable,
348 public sigc::trackable351 public sigc::trackable
349{352{
350public:353public:
@@ -391,6 +394,7 @@
391394
392 bool place(CompPoint& pos);395 bool place(CompPoint& pos);
393 CompPoint tryNotIntersectUI(CompPoint& pos);396 CompPoint tryNotIntersectUI(CompPoint& pos);
397 nux::Geometry GetScaledGeometry();
394398
395 void paintThumbnail (nux::Geometry const& bounding, float alpha);399 void paintThumbnail (nux::Geometry const& bounding, float alpha);
396400
@@ -413,6 +417,10 @@
413 const CompRegion &,417 const CompRegion &,
414 unsigned int);418 unsigned int);
415419
420protected:
421 std::string GetName() const;
422 void AddProperties(GVariantBuilder* builder);
423
416private:424private:
417 struct CairoContext;425 struct CairoContext;
418426
@@ -449,23 +457,16 @@
449457
450 compiz::WindowInputRemoverLock::Ptr GetInputRemover ();458 compiz::WindowInputRemoverLock::Ptr GetInputRemover ();
451459
452 void DrawWindowDecoration(GLWindowPaintAttrib const& attrib,460 void DrawWindowDecoration(GLWindowPaintAttrib const& attrib, GLMatrix const& transform,
453 GLMatrix const& transform,461 unsigned int mask, bool highlighted,
454 unsigned int mask,
455 bool highlighted,
456 int x, int y, unsigned width, unsigned height);462 int x, int y, unsigned width, unsigned height);
457 void DrawTexture(GLTexture *icon,463 void DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const& attrib,
458 const GLWindowPaintAttrib& attrib,464 GLMatrix const& transform, unsigned int mask, int x, int y);
459 const GLMatrix& transform,465 void RenderText(CairoContext const& context, int x, int y, int width, int height);
460 unsigned int mask,
461 float x, float y,
462 int &maxWidth, int &maxHeight);
463 void RenderText(CairoContext const& context,
464 float x, float y,
465 float maxWidth, float maxHeight);
466466
467 void SetupScaleHeaderStyle();467 static void SetupSharedTextures();
468 void LoadCloseIcon(panel::WindowState state, GLTexture::List& texture);468 static void CleanupSharedTextures();
469 static void LoadCloseIcon(panel::WindowState state, GLTexture::List& texture);
469470
470 static GLTexture::List close_normal_tex_;471 static GLTexture::List close_normal_tex_;
471 static GLTexture::List close_prelight_tex_;472 static GLTexture::List close_prelight_tex_;
@@ -473,7 +474,10 @@
473 compiz::WindowInputRemoverLock::Weak input_remover_;474 compiz::WindowInputRemoverLock::Weak input_remover_;
474 panel::WindowState close_icon_state_;475 panel::WindowState close_icon_state_;
475 nux::Geometry close_button_geo_;476 nux::Geometry close_button_geo_;
477 bool middle_clicked_;
476 glib::Source::UniquePtr focus_desktop_timeout_;478 glib::Source::UniquePtr focus_desktop_timeout_;
479
480 friend class UnityScreen;
477};481};
478482
479483
480484
=== added file 'tests/autopilot/unity/emulators/screen.py'
--- tests/autopilot/unity/emulators/screen.py 1970-01-01 00:00:00 +0000
+++ tests/autopilot/unity/emulators/screen.py 2012-09-24 16:14:49 +0000
@@ -0,0 +1,42 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2012 Canonical
3# Author: Marco Trevisan (Treviño)
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9
10from __future__ import absolute_import
11
12import logging
13from unity.emulators import UnityIntrospectionObject
14logger = logging.getLogger(__name__)
15
16
17class Screen(UnityIntrospectionObject):
18 """The Screen class."""
19
20 @property
21 def windows(self):
22 """Return the available windows, or None."""
23 return self.get_children_by_type(Window)
24
25 @property
26 def scaled_windows(self):
27 """Return the available scaled windows, or None."""
28 return self.get_children_by_type(Window, scaled=True)
29
30
31class Window(UnityIntrospectionObject):
32 """An individual window."""
33
34 @property
35 def geometry(self):
36 """Returns a tuple of (x,y,w,h) for the current window."""
37 return (self.x, self.y, self.width, self.height)
38
39 @property
40 def scale_close_geometry(self):
41 """Returns a tuple of (x,y,w,h) for the scale close button."""
42 return (self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height)
043
=== modified file 'tests/autopilot/unity/tests/__init__.py'
--- tests/autopilot/unity/tests/__init__.py 2012-07-12 20:54:59 +0000
+++ tests/autopilot/unity/tests/__init__.py 2012-09-24 16:14:49 +0000
@@ -20,6 +20,7 @@
20from testtools.content import text_content20from testtools.content import text_content
21from testtools.matchers import Equals21from testtools.matchers import Equals
2222
23from unity.emulators.screen import Screen
23from unity.emulators.dash import Dash24from unity.emulators.dash import Dash
24from unity.emulators.hud import Hud25from unity.emulators.hud import Hud
25from unity.emulators.launcher import LauncherController26from unity.emulators.launcher import LauncherController
@@ -114,6 +115,12 @@
114 return True115 return True
115116
116 @property117 @property
118 def screen(self):
119 if not getattr(self, '__screen', None):
120 self.__screen = self._get_screen()
121 return self.__screen
122
123 @property
117 def dash(self):124 def dash(self):
118 if not getattr(self, '__dash', None):125 if not getattr(self, '__dash', None):
119 self.__dash = Dash()126 self.__dash = Dash()
@@ -155,6 +162,11 @@
155 self.__workspace = WorkspaceManager()162 self.__workspace = WorkspaceManager()
156 return self.__workspace163 return self.__workspace
157164
165 def _get_screen(self):
166 screens = Screen.get_all_instances()
167 self.assertThat(len(screens), Equals(1))
168 return screens[0]
169
158 def _get_launcher_controller(self):170 def _get_launcher_controller(self):
159 controllers = LauncherController.get_all_instances()171 controllers = LauncherController.get_all_instances()
160 self.assertThat(len(controllers), Equals(1))172 self.assertThat(len(controllers), Equals(1))
161173
=== added file 'tests/autopilot/unity/tests/test_spread.py'
--- tests/autopilot/unity/tests/test_spread.py 1970-01-01 00:00:00 +0000
+++ tests/autopilot/unity/tests/test_spread.py 2012-09-24 16:14:49 +0000
@@ -0,0 +1,124 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2012 Canonical
3# Author: Marco Trevisan (Treviño)
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8
9from __future__ import absolute_import
10
11from autopilot.matchers import Eventually
12import logging
13from time import sleep
14from testtools.matchers import Equals, NotEquals
15
16from unity.tests import UnityTestCase
17from unity.emulators.screen import Screen
18
19
20class SpreadTests(UnityTestCase):
21 """Spread tests"""
22
23 def start_test_application_windows(self, app_name, num_windows=2):
24 """Start a given number of windows of the requested application"""
25 self.close_all_app(app_name)
26 windows = []
27
28 for i in range(num_windows):
29 win = self.start_app_window(app_name)
30 if len(windows):
31 self.assertThat(win.application, Equals(windows[-1].application))
32
33 windows.append(win)
34
35 self.assertThat(len(windows), Equals(num_windows))
36
37 return windows
38
39 def initiate_spread_for_screen(self):
40 """Initiate the Spread for all windows"""
41 self.addCleanup(self.keybinding, "spread/cancel")
42 self.keybinding("spread/start")
43 sleep(1)
44 self.assertThat(self.window_manager.scale_active, Eventually(Equals(True)))
45
46
47 def initiate_spread_for_application(self, desktop_id):
48 """Initiate the Spread for windows of the given app"""
49 icon = self.launcher.model.get_icon(desktop_id=desktop_id)
50 self.assertThat(lambda: icon, Eventually(NotEquals(None)))
51 launcher = self.launcher.get_launcher_for_monitor(self.screen_geo.get_primary_monitor())
52
53 self.addCleanup(self.keybinding, "spread/cancel")
54 launcher.click_launcher_icon(icon)
55 self.assertThat(self.window_manager.scale_active_for_group, Eventually(Equals(True)))
56
57 def assertWindowIsNotScaled(self, xid):
58 """Assert that a window is not scaled"""
59 refresh_fn = lambda: xid in [w.xid for w in self.screen.scaled_windows]
60 self.assertThat(refresh_fn, Eventually(Equals(False)))
61
62 def assertWindowIsClosed(self, xid):
63 """Assert that a window is not in the list of the open windows"""
64 refresh_fn = lambda: xid in [w.x_id for w in self.bamf.get_open_windows()]
65 self.assertThat(refresh_fn, Eventually(Equals(False)))
66
67
68 def test_scale_application_windows(self):
69 """Test if all the windows of an application are scaled when application spread is initiated"""
70 [win1, win2] = self.start_test_application_windows("Calculator")
71 self.initiate_spread_for_application(win1.application.desktop_file)
72
73 self.assertThat(lambda: len(self.screen.scaled_windows), Eventually(Equals(2)))
74 self.assertThat(lambda: (win1.x_id and win2.x_id) in [w.xid for w in self.screen.scaled_windows],
75 Eventually(Equals(True)))
76
77 def test_scaled_window_is_focused_on_click(self):
78 """Test that a window is focused when clicked in spread"""
79 windows = self.start_test_application_windows("Calculator", 3)
80 self.initiate_spread_for_application(windows[0].application.desktop_file)
81
82 not_focused = [w for w in windows if not w.is_focused][0]
83
84 target_xid = not_focused.x_id
85 [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid]
86
87 (x, y, w, h) = target_win.geometry
88 self.mouse.move(x + w / 2, y + h / 2)
89 sleep(.5)
90 self.mouse.click()
91
92 self.assertThat(lambda: not_focused.is_focused, Eventually(Equals(True)))
93
94 def test_scaled_window_closes_on_middle_click(self):
95 """Test that a window is closed when middle-clicked in spread"""
96 win = self.start_test_application_windows("Calculator", 2)[0]
97 self.initiate_spread_for_application(win.application.desktop_file)
98
99 target_xid = win.x_id
100 [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid]
101
102 (x, y, w, h) = target_win.geometry
103 self.mouse.move(x + w / 2, y + h / 2)
104 sleep(.5)
105 self.mouse.click(button=2)
106
107 self.assertWindowIsNotScaled(target_xid)
108 self.assertWindowIsClosed(target_xid)
109
110 def test_scaled_window_closes_on_close_button_click(self):
111 """Test that a window is closed when its close button is clicked in spread"""
112 win = self.start_test_application_windows("Calculator", 1)[0]
113 self.initiate_spread_for_screen()
114
115 target_xid = win.x_id
116 [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid]
117
118 (x, y, w, h) = target_win.scale_close_geometry
119 self.mouse.move(x + w / 2, y + h / 2)
120 sleep(.5)
121 self.mouse.click()
122
123 self.assertWindowIsNotScaled(target_xid)
124 self.assertWindowIsClosed(target_xid)