diff options
23 files changed, 975 insertions, 361 deletions
diff --git a/dash/ResultViewGrid.cpp b/dash/ResultViewGrid.cpp index efc42b21c..840455f0c 100644 --- a/dash/ResultViewGrid.cpp +++ b/dash/ResultViewGrid.cpp @@ -347,6 +347,8 @@ bool ResultViewGrid::InspectKeyEvent(unsigned int eventType, unsigned int keysym case NUX_KP_ENTER: direction = nux::KeyNavDirection::KEY_NAV_ENTER; break; + case XK_Menu: + return true; default: direction = nux::KeyNavDirection::KEY_NAV_NONE; break; @@ -478,6 +480,16 @@ void ResultViewGrid::OnKeyDown (unsigned long event_type, unsigned long event_ke ubus_.SendMessage(UBUS_RESULT_VIEW_KEYNAV_CHANGED, g_variant_new("(iiii)", focused_x, focused_y, renderer_->width(), renderer_->height())); selection_change.emit(); + + if (event_type == nux::NUX_KEYDOWN && event_keysym == XK_Menu) + { + UriActivated.emit (focused_uri_, ResultView::ActivateType::PREVIEW); + int left_results = selected_index_; + int right_results = (num_results - selected_index_) - 1; + //FIXME - just uses y right now, needs to use the absolute position of the bottom of the result + ubus_.SendMessage(UBUS_DASH_PREVIEW_INFO_PAYLOAD, + g_variant_new("(iii)", mouse_last_y_, left_results, right_results)); + } } nux::Area* ResultViewGrid::KeyNavIteration(nux::KeyNavDirection direction) diff --git a/dash/previews/ActionButton.cpp b/dash/previews/ActionButton.cpp index 03e325693..7c392787f 100644 --- a/dash/previews/ActionButton.cpp +++ b/dash/previews/ActionButton.cpp @@ -77,11 +77,6 @@ void ActionButton::Init() { InitTheme(); - key_nav_focus_change.connect([&] (nux::Area*, bool, nux::KeyNavDirection) - { - QueueDraw(); - }); - key_nav_focus_activate.connect([&](nux::Area*) { if (GetInputEventSensitivity()) diff --git a/launcher/BamfLauncherIcon.cpp b/launcher/BamfLauncherIcon.cpp index f24529f30..3bd8f3c51 100644 --- a/launcher/BamfLauncherIcon.cpp +++ b/launcher/BamfLauncherIcon.cpp @@ -630,35 +630,38 @@ std::vector<Window> BamfLauncherIcon::GetFocusableWindows(ActionArg arg, bool &a std::vector<Window> windows; GList* children; - BamfView *focusable_child = BAMF_VIEW (bamf_application_get_focusable_child (_bamf_app.RawPtr())); + BamfView *focusable_child = BAMF_VIEW(bamf_application_get_focusable_child(_bamf_app.RawPtr())); if (focusable_child != NULL) - { - Window xid; - - if (BAMF_IS_WINDOW (focusable_child)) - xid = bamf_window_get_xid (BAMF_WINDOW(focusable_child)); - else if (BAMF_IS_TAB (focusable_child)) - { - BamfTab *focusable_tab = BAMF_TAB (focusable_child); - - xid = bamf_tab_get_xid (focusable_tab); + { + Window xid = 0; - bamf_tab_raise (focusable_tab); - } + if (BAMF_IS_WINDOW(focusable_child)) + { + xid = bamf_window_get_xid(BAMF_WINDOW(focusable_child)); + } + else if (BAMF_IS_TAB(focusable_child)) + { + BamfTab *focusable_tab = BAMF_TAB(focusable_child); + xid = bamf_tab_get_xid(focusable_tab); + bamf_tab_raise(focusable_tab); + } + if (xid) + { windows.push_back(xid); return windows; } + } else + { + if (g_strcmp0(bamf_application_get_application_type(_bamf_app.RawPtr()), "webapp") == 0) { - if (g_strcmp0 (bamf_application_get_application_type (_bamf_app.RawPtr()), "webapp") == 0) - { - OpenInstanceLauncherIcon(arg); + OpenInstanceLauncherIcon(arg); - return windows; - } + return windows; } + } children = bamf_view_get_children(BAMF_VIEW(_bamf_app.RawPtr())); @@ -704,7 +707,7 @@ std::vector<Window> BamfLauncherIcon::GetFocusableWindows(ActionArg arg, bool &a } g_list_free(children); - + return windows; } diff --git a/manual-tests/Switcher.txt b/manual-tests/Switcher.txt index 6187d3564..a48fe7a08 100644 --- a/manual-tests/Switcher.txt +++ b/manual-tests/Switcher.txt @@ -103,3 +103,19 @@ Actions: Expected Result: The assertions from above hold. + + +Alt+Tab doesn't lose window focus +--------------------------------- +This tests ensures that a racing condition is not possible anymore when alt+tabing. + +Setup: +#. Have 2 windows of different types open. (gedit, nautilus) + +Actions: + #. Press quicky Alt+Tab. (so that the switcher window is not shown) + #. Repeat 10 times. + +Expected Results: + No window loses focuses at all. The panel should alway show the title of the + window. diff --git a/plugins/unityshell/resources/emblem_apps.svg b/plugins/unityshell/resources/emblem_apps.svg index 6af364db0..53bfec85e 100644 --- a/plugins/unityshell/resources/emblem_apps.svg +++ b/plugins/unityshell/resources/emblem_apps.svg @@ -1,19 +1,90 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="14px" height="14px" viewBox="0 0 14 14" enable-background="new 0 0 14 14" xml:space="preserve"> -<path fill="#DC4A26" d="M8.5,4.504v7.997c0,0.55-0.469,0.999-1.018,0.999H6.498c-0.55,0-0.998-0.449-0.998-0.999V4.504H8.5z"/> -<g> - <path fill="#DC4A26" d="M4.5,4.504h-2v-1h2V1.505c0-0.55-0.45-1-1-1H1.501c-0.55,0-1,0.45-1,1L0.5,12.501 - c0,0.55,0.45,0.999,1,0.999h1.999c0.55,0,1-0.449,1-0.999V10.5L2.5,10.502v-1L4.499,9.5V7.503H2.5v-1h2"/> -</g> -<path fill="#DC4A26" d="M4.5,6.503v-1l0,0V6.503L4.5,6.503z"/> -<path fill="#DC4A26" d="M4.5,9.502v-1h0L4.5,9.502L4.5,9.502z"/> -<path fill="#DC4A26" d="M4.5,12.499L4.5,12.499L4.5,12.499L4.5,12.499L4.5,12.499z"/> -<path fill="#DC4A26" d="M11.866,0.508v1.237C12.102,1.856,12.3,2.052,12.3,2.286c0,0.354-0.358,0.64-0.8,0.64s-0.8-0.286-0.8-0.64 - c0-0.234,0.198-0.43,0.433-0.541V0.508C10.66,0.972,9.5,2.321,9.5,2.917c0,0.884,1.116,1.6,2,1.6s2-0.716,2-1.6 - C13.5,2.321,12.34,0.972,11.866,0.508z"/> -<path fill="#DC4A26" d="M5.501,3.499L7.047,0.5L8.5,3.499H5.501z"/> -<path fill="#DC4A26" d="M13.5,5.503v6.998c0,0.55-0.45,0.999-1,0.999h-2c-0.55,0-1-0.449-1-0.999V5.503H13.5z"/> -</svg> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Layer_1" + x="0px" + y="0px" + width="14px" + height="14px" + viewBox="0 0 14 14" + enable-background="new 0 0 14 14" + xml:space="preserve" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="emblem_apps.svg"><metadata + id="metadata25"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs23" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="879" + inkscape:window-height="476" + id="namedview21" + showgrid="false" + inkscape:zoom="16.857143" + inkscape:cx="7.0207627" + inkscape:cy="7" + inkscape:window-x="65" + inkscape:window-y="24" + inkscape:window-maximized="0" + inkscape:current-layer="Layer_1" /> +<path + d="m 8,5 v 8.000556 C 8,13.5508 7.531,14 6.982,14 H 5.998 C 5.448,14 5,13.5508 5,13.000556 V 5 h 3 z" + id="path3" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> +<g + id="g5" + transform="matrix(1,0,0,1.0003848,-0.5,0.49480569)"> + <path + d="m 4.5,4.504 h -2 v -1 h 2 V 1.505 c 0,-0.55 -0.45,-1 -1,-1 H 1.501 c -0.55,0 -1,0.45 -1,1 L 0.5,12.501 c 0,0.55 0.45,0.999 1,0.999 h 1.999 c 0.55,0 1,-0.449 1,-0.999 V 10.5 L 2.5,10.502 v -1 L 4.499,9.5 V 7.503 H 2.5 v -1 h 2" + id="path7" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> +</g> +<path + d="m 4,7.003 v -1 l 0,0 v 1 l 0,0 z" + id="path9" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> +<path + d="m 4,10.002 v -1 h 0 l 0,1 0,0 z" + id="path11" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> +<path + d="m 4,12.999 0,0 0,0 0,0 0,0 z" + id="path13" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> +<path + d="m 11.366,1 v 1.234223 c 0.236,0.1107508 0.434,0.3063108 0.434,0.5397855 0,0.3532053 -0.358,0.6385632 -0.8,0.6385632 -0.442,0 -0.8,-0.2853579 -0.8,-0.6385632 0,-0.2334747 0.198,-0.4290347 0.433,-0.5397855 V 1 C 10.16,1.4629583 9,2.8089299 9,3.4035919 9,4.2856074 10.116,5 11,5 11.884,5 13,4.2856074 13,3.4035919 13,2.8089299 11.84,1.4629583 11.366,1 z" + id="path15" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> +<path + d="M 5,4 6.5465155,1 8,4 H 5 z" + id="path17" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> +<path + d="m 13,6 v 7.000625 C 13,13.550832 12.55,14 12,14 H 10 C 9.45,14 9,13.550832 9,13.000625 V 6 h 4 z" + id="path19" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> +</svg> \ No newline at end of file diff --git a/plugins/unityshell/resources/emblem_others.svg b/plugins/unityshell/resources/emblem_others.svg new file mode 100644 index 000000000..cb6c7ed24 --- /dev/null +++ b/plugins/unityshell/resources/emblem_others.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="14px" height="14px" viewBox="0 0 14 14" enable-background="new 0 0 14 14" xml:space="preserve"> +<g> + <path fill="#DC4A26" d="M1.667,5C1.298,5,1,5.298,1,5.667v6.667C1,12.702,1.298,13,1.667,13h10.667C12.702,13,13,12.702,13,12.334 + V5.667C13,5.298,12.702,5,12.334,5H10.5C10.097,3.775,8.947,1,7,1C5.054,1,3.902,3.775,3.5,5H1.667z M4.5,5C4.923,3.757,5.84,2,7,2 + s2.076,1.757,2.5,3H4.5z M4.001,8C3.446,8,3,7.555,3,7c0-0.551,0.446-1,1.001-1C4.554,6,5,6.449,5,7C5,7.555,4.554,8,4.001,8z + M10,8.001C9.447,8.001,9,7.554,9,7c0-0.553,0.447-1.001,1-1.001S11,6.447,11,7C11,7.554,10.553,8.001,10,8.001z"/> +</g> +</svg> diff --git a/plugins/unityshell/resources/emblem_video.svg b/plugins/unityshell/resources/emblem_video.svg index a8ad76142..a604feedd 100644 --- a/plugins/unityshell/resources/emblem_video.svg +++ b/plugins/unityshell/resources/emblem_video.svg @@ -1,17 +1,58 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" - width="14px" height="14px" viewBox="0 0 14 14" enable-background="new 0 0 14 14" xml:space="preserve"> -<g> - <g> - <path fill="#DC4A26" d="M12.301,2.5H1.7C1.315,2.5,1,2.813,1,3.196v7.608C1,11.188,1.315,11.5,1.7,11.5h10.601 - c0.385,0,0.699-0.312,0.699-0.696V3.196C13,2.813,12.686,2.5,12.301,2.5z M2.989,10.455H1.994V9.498h0.996V10.455z M2.989,8.49 - H1.994V7.5h0.996V8.49z M2.989,6.494H1.994V5.535h0.996V6.494z M2.989,4.496H1.994V3.538h0.996V4.496z M8.006,7.841 - C7.659,8.126,7.308,8.404,6.954,8.677C6.6,8.947,6.255,9.197,5.92,9.426c-0.335,0.229-0.64,0.422-0.913,0.579V4.026 - c0.261,0.158,0.559,0.35,0.894,0.578c0.335,0.229,0.68,0.476,1.034,0.74c0.354,0.265,0.708,0.54,1.061,0.825 - c0.355,0.286,0.687,0.565,0.998,0.836C8.684,7.276,8.354,7.555,8.006,7.841z M11.992,10.455h-0.996V9.498h0.996V10.455z - M11.992,8.49h-0.996V7.5h0.996V8.49z M11.992,6.494h-0.996V5.535h0.996V6.494z M11.992,4.496h-0.996V3.538h0.996V4.496z"/> - </g> -</g> -</svg> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Layer_1" + x="0px" + y="0px" + width="14px" + height="14px" + viewBox="0 0 14 14" + enable-background="new 0 0 14 14" + xml:space="preserve" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="emblem_video.svg"><metadata + id="metadata13"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs11" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1215" + inkscape:window-height="776" + id="namedview9" + showgrid="false" + inkscape:zoom="16.857143" + inkscape:cx="7" + inkscape:cy="7" + inkscape:window-x="65" + inkscape:window-y="24" + inkscape:window-maximized="1" + inkscape:current-layer="Layer_1" /> +<g + id="g3" + transform="translate(0,0.5)"> + <g + id="g5"> + <path + d="M 12.301,2.5 H 1.7 C 1.315,2.5 1,2.813 1,3.196 v 7.608 C 1,11.188 1.315,11.5 1.7,11.5 H 12.301 C 12.686,11.5 13,11.188 13,10.804 V 3.196 C 13,2.813 12.686,2.5 12.301,2.5 z M 2.989,10.455 H 1.994 V 9.498 H 2.99 v 0.957 z m 0,-1.965 H 1.994 V 7.5 H 2.99 v 0.99 z m 0,-1.996 H 1.994 V 5.535 H 2.99 v 0.959 z m 0,-1.998 H 1.994 V 3.538 H 2.99 V 4.496 z M 8.006,7.841 C 7.659,8.126 7.308,8.404 6.954,8.677 6.6,8.947 6.255,9.197 5.92,9.426 5.585,9.655 5.28,9.848 5.007,10.005 V 4.026 c 0.261,0.158 0.559,0.35 0.894,0.578 0.335,0.229 0.68,0.476 1.034,0.74 0.354,0.265 0.708,0.54 1.061,0.825 0.355,0.286 0.687,0.565 0.998,0.836 -0.31,0.271 -0.64,0.55 -0.988,0.836 z m 3.986,2.614 H 10.996 V 9.498 h 0.996 v 0.957 z m 0,-1.965 H 10.996 V 7.5 h 0.996 v 0.99 z m 0,-1.996 H 10.996 V 5.535 h 0.996 v 0.959 z m 0,-1.998 H 10.996 V 3.538 h 0.996 v 0.958 z" + id="path7" + inkscape:connector-curvature="0" + style="fill:#dc4a26" /> + </g> +</g> +</svg> \ No newline at end of file diff --git a/plugins/unityshell/resources/lens-nav-gwibber.svg b/plugins/unityshell/resources/lens-nav-gwibber.svg index 07361aa25..c0d95ad68 100644 --- a/plugins/unityshell/resources/lens-nav-gwibber.svg +++ b/plugins/unityshell/resources/lens-nav-gwibber.svg @@ -3,12 +3,10 @@ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> -<path fill="#FFFFFF" d="M23,11.38c-0.774,0.14-1.897-0.005-2.492-0.267c1.236-0.108,2.073-0.701,2.396-1.505 - c-0.445,0.29-1.829,0.604-2.593,0.304c-0.038-0.189-0.078-0.369-0.121-0.533c-0.581-2.256-2.576-4.074-4.663-3.854 - c0.169-0.073,0.34-0.139,0.512-0.2c0.229-0.087,1.577-0.318,1.365-0.82c-0.179-0.442-1.828,0.332-2.138,0.435 - c0.41-0.162,1.087-0.441,1.159-0.939c-0.627,0.091-1.243,0.404-1.72,0.86c0.173-0.195,0.304-0.432,0.331-0.689 - c-1.675,1.129-2.652,3.405-3.443,5.613c-0.621-0.637-1.173-1.136-1.667-1.416C8.541,7.584,6.883,6.766,4.282,5.746 - c-0.08,0.909,0.426,2.118,1.881,2.92C5.848,8.621,5.271,8.722,4.811,8.838c0.188,1.042,0.801,1.899,2.463,2.312 - c-0.759,0.053-1.153,0.237-1.508,0.629c0.346,0.725,1.191,1.576,2.708,1.401c-1.689,0.769-0.688,2.19,0.686,1.979 - C6.817,17.715,3.122,17.525,1,15.39c5.539,7.969,17.581,4.712,19.374-2.963C21.721,12.438,22.51,11.936,23,11.38z"/> +<path fill="#FFFFFF" d="M12.635,13.608c-0.849,0.836-2.011,1.265-3.272,1.204l0,0c-0.413-0.02-0.826-0.091-1.229-0.215 + c-0.368-0.112-0.665-0.404-0.778-0.769c-0.509-1.623-0.123-3.323,1.009-4.437c0.85-0.836,2.011-1.264,3.272-1.205 + c0.411,0.02,0.826,0.093,1.228,0.215c0.368,0.113,0.665,0.405,0.778,0.769C14.153,10.794,13.766,12.492,12.635,13.608z M15.5,7 + C15.224,7,15,6.775,15,6.499C15,6.224,15.224,6,15.5,6S16,6.224,16,6.499C16,6.775,15.776,7,15.5,7z M19.986,5.999 M20,6 + c0.552,0,1-0.447,1-1c0-0.552-0.449-1-1-1h-9c-4.418,0-8,3.582-8,8c0,4.418,3.582,8,8,8c4.417,0,8-3.576,8-7.994V7 + c0-0.01,0.001-0.019,0.001-0.028C19.001,6.436,19.448,6,20,6"/> </svg> diff --git a/plugins/unityshell/src/ScreenIntrospection.h b/plugins/unityshell/src/ScreenIntrospection.h new file mode 100644 index 000000000..5f0c6282f --- /dev/null +++ b/plugins/unityshell/src/ScreenIntrospection.h @@ -0,0 +1,48 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2012 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#ifndef SCREEN_INTROSPECTION_H +#define SCREEN_INTROSPECTION_H + +#include "Introspectable.h" +#include <core/core.h> + +namespace unity +{ +namespace debug +{ + +class ScreenIntrospection : public Introspectable +{ +public: + ScreenIntrospection(CompScreen* screen); + +protected: + std::string GetName() const; + void AddProperties(GVariantBuilder* builder); + IntrospectableList GetIntrospectableChildren(); + +private: + CompScreen* screen_; +}; + +} +} + +#endif diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index 50851f52c..8e49edb0f 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -26,6 +26,8 @@ #include <Nux/WindowCompositor.h> #include <Nux/NuxTimerTickSource.h> +#include <UnityCore/Variant.h> + #include "BaseWindowRaiserImp.h" #include "IconRenderer.h" #include "Launcher.h" @@ -85,9 +87,6 @@ nux::logging::Logger logger("unity.shell"); UnityScreen* uScreen = 0; -const unsigned int SCALE_CLOSE_ICON_SIZE = 19; -const unsigned int SCALE_ITEMS_PADDING = 5; - void reset_glib_logging(); void configure_logging(); void capture_g_log_calls(const gchar* log_domain, @@ -109,8 +108,8 @@ const std::string RELAYOUT_TIMEOUT = "relayout-timeout"; } // anon namespace UnityScreen::UnityScreen(CompScreen* screen) - : BaseSwitchScreen (screen) - , PluginClassHandler <UnityScreen, CompScreen> (screen) + : BaseSwitchScreen(screen) + , PluginClassHandler <UnityScreen, CompScreen>(screen) , screen(screen) , cScreen(CompositeScreen::get(screen)) , gScreen(GLScreen::get(screen)) @@ -125,7 +124,6 @@ UnityScreen::UnityScreen(CompScreen* screen) , allowWindowPaint(false) , _key_nav_mode_requested(false) , _last_output(nullptr) - , _bghash(NULL) , grab_index_ (0) , painting_tray_ (false) , last_scroll_event_(0) @@ -133,7 +131,7 @@ UnityScreen::UnityScreen(CompScreen* screen) , panel_texture_has_changed_(true) , paint_panel_(false) , scale_just_activated_(false) - , minimize_speed_controller(new WindowMinimizeSpeedController()) + , screen_introspection_(screen) { Timer timer; #ifndef USE_GLES @@ -244,7 +242,7 @@ UnityScreen::UnityScreen(CompScreen* screen) // _bghash is a pointer. We don't want it to be created before Nux system has had a chance // to start. BGHash relies on animations. Nux animation system starts after the WindowThread // has been created. - _bghash = new BGHash(); + _bghash.reset(new BGHash()); unity_a11y_init(wt.get()); @@ -375,13 +373,15 @@ UnityScreen::UnityScreen(CompScreen* screen) Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());; XSelectInput(display, GDK_ROOT_WINDOW(), PropertyChangeMask); LOG_INFO(logger) << "UnityScreen constructed: " << timer.ElapsedSeconds() << "s"; - } - panel::Style::Instance().changed.connect(sigc::mem_fun(this, &UnityScreen::OnPanelStyleChanged)); + panel::Style::Instance().changed.connect(sigc::mem_fun(this, &UnityScreen::OnPanelStyleChanged)); - minimize_speed_controller->DurationChanged.connect( - sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged) - ); + minimize_speed_controller_.DurationChanged.connect( + sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged) + ); + + AddChild(&screen_introspection_); + } } UnityScreen::~UnityScreen() @@ -391,7 +391,6 @@ UnityScreen::~UnityScreen() unity_a11y_finalize(); ::unity::ui::IconRenderer::DestroyTextures(); QuicklistManager::Destroy(); - delete _bghash; reset_glib_logging(); } @@ -659,10 +658,20 @@ UnityWindow::updateIconPos (int &wx, wy = y + (last_bound.height - height) / 2; } -void -UnityScreen::OnPanelStyleChanged() +void UnityScreen::OnPanelStyleChanged() { panel_texture_has_changed_ = true; + + // Reload the windows themed textures + UnityWindow::CleanupSharedTextures(); + + if (WindowManager::Default()->IsScaleActive()) + { + UnityWindow::SetupSharedTextures(); + + for (auto const& swin : ScaleScreen::get(screen)->getWindows()) + UnityWindow::get(swin->window)->CleanupCachedTextures(); + } } void UnityScreen::paintDisplay() @@ -1136,6 +1145,12 @@ bool UnityWindow::handleEvent(XEvent *event) DoAddDamage(); handled = true; } + else if (event->xbutton.button == Button2 && + GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root)) + { + middle_clicked_ = true; + handled = true; + } break; case ButtonRelease: @@ -1155,6 +1170,18 @@ bool UnityWindow::handleEvent(XEvent *event) handled = true; } + + if (middle_clicked_) + { + if (event->xbutton.button == Button2 && + GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root)) + { + window->close(0); + } + + middle_clicked_ = false; + handled = true; + } } break; @@ -2059,7 +2086,8 @@ void UnityScreen::OnLauncherEndKeyNav(GVariant* data) void UnityScreen::OnSwitcherStart(GVariant* data) { - SaveInputThenFocus(switcher_controller_->GetSwitcherInputWindowId()); + if (switcher_controller_->Visible()) + SaveInputThenFocus(switcher_controller_->GetSwitcherInputWindowId()); } void UnityScreen::OnSwitcherEnd(GVariant* data) @@ -2277,8 +2305,7 @@ bool UnityScreen::initPluginForScreen(CompPlugin* p) } void UnityScreen::AddProperties(GVariantBuilder* builder) -{ -} +{} std::string UnityScreen::GetName() const { @@ -2469,10 +2496,10 @@ UnityScreen::OnMinimizeDurationChanged () CompOption::Value& value = o.value(); CompOption::Value::Vector& list = value.list(); CompOption::Value::Vector::iterator i = list.begin(); - if (i != list.end()) { - i->set(minimize_speed_controller->getDuration()); - } - value.set(list); + if (i != list.end()) + i->set(minimize_speed_controller_.getDuration()); + + value.set(list); screen->setOptionForPlugin(p->vTable->name().c_str(), o.name().c_str(), value); break; @@ -2603,7 +2630,7 @@ void UnityWindow::windowNotify(CompWindowNotify n) case CompWindowNotifyMinimize: /* Updating the count in dconf will trigger a "changed" signal to which * the method setting the new animation speed is attached */ - UnityScreen::get(screen)->minimize_speed_controller->UpdateCount(); + UnityScreen::get(screen)->minimize_speed_controller_.UpdateCount(); break; default: break; @@ -3356,11 +3383,23 @@ GLTexture::List UnityWindow::close_normal_tex_; GLTexture::List UnityWindow::close_prelight_tex_; GLTexture::List UnityWindow::close_pressed_tex_; +namespace scale +{ +namespace decoration +{ +const unsigned CLOSE_SIZE = 19; +const unsigned ITEMS_PADDING = 5; +const unsigned RADIUS = 8; +} +} + struct UnityWindow::CairoContext { - CairoContext(int width, int height) - : pixmap_(XCreatePixmap(screen->dpy(), screen->root(), width, height, 32)) - , texture_(GLTexture::bindPixmapToTexture(pixmap_, width, height, 32)) + CairoContext(unsigned width, unsigned height) + : w_(width) + , h_(height) + , pixmap_(XCreatePixmap(screen->dpy(), screen->root(), w_, h_, 32)) + , texture_(GLTexture::bindPixmapToTexture(pixmap_, w_, h_, 32)) , surface_(nullptr) , cr_(nullptr) { @@ -3382,7 +3421,7 @@ struct UnityWindow::CairoContext cairo_restore(cr_); } - ~CairoContext () + ~CairoContext() { if (cr_) cairo_destroy(cr_); @@ -3390,12 +3429,12 @@ struct UnityWindow::CairoContext if (surface_) cairo_surface_destroy(surface_); - texture_.clear(); - if (pixmap_) - XFreePixmap(screen->dpy (), pixmap_); + XFreePixmap(screen->dpy(), pixmap_); } + unsigned w_; + unsigned h_; Pixmap pixmap_; GLTexture::List texture_; cairo_surface_t* surface_; @@ -3454,111 +3493,150 @@ UnityWindow::UnityWindow(CompWindow* window) } } - WindowManager::Default()->initiate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnInitiateSpreed)); - WindowManager::Default()->terminate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnTerminateSpreed)); + WindowManager::Default()->initiate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnInitiateSpread)); + WindowManager::Default()->terminate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnTerminateSpread)); } -void -UnityWindow::DrawTexture(GLTexture* icon, - const GLWindowPaintAttrib& attrib, - const GLMatrix& transform, - unsigned int mask, - float x, float y, - int &maxWidth, int &maxHeight) +void UnityWindow::AddProperties(GVariantBuilder* builder) { - if (icon) - { - int width, height; - width = icon->width(); - height = icon->height(); + Window xid = window->id(); + auto const& swins = ScaleScreen::get(screen)->getWindows(); + bool scaled = std::find(swins.begin(), swins.end(), ScaleWindow::get(window)) != swins.end(); + auto wm = WindowManager::Default(); - if (height > maxHeight) - maxHeight = height; + variant::BuilderWrapper(builder) + .add(scaled ? GetScaledGeometry() : wm->GetWindowGeometry(xid)) + .add("xid", xid) + .add("title", wm->GetWindowName(xid)) + .add("scaled", scaled) + .add("scaled_close_x", close_button_geo_.x) + .add("scaled_close_y", close_button_geo_.y) + .add("scaled_close_width", close_button_geo_.width) + .add("scaled_close_height", close_button_geo_.height); +} + +std::string UnityWindow::GetName() const +{ + return "Window"; +} - if (width > maxWidth) - maxWidth = width; - CompRegion iconReg(0, 0, width, height); - GLTexture::MatrixList ml(1); +void UnityWindow::DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const& attrib, + GLMatrix const& transform, unsigned int mask, int x, int y, double scale) +{ + for (auto const& texture : textures) + { + if (!texture) + continue; - ml[0] = icon->matrix(); gWindow->vertexBuffer()->begin(); - if (width && height) - gWindow->glAddGeometry(ml, iconReg, iconReg); + + if (texture->width() && texture->height()) + { + GLTexture::MatrixList ml(1); + ml[0] = texture->matrix(); + CompRegion texture_region(0, 0, texture->width(), texture->height()); + gWindow->glAddGeometry(ml, texture_region, texture_region); + } if (gWindow->vertexBuffer()->end()) { GLMatrix wTransform(transform); - wTransform.translate(x, y, 0.0f); + wTransform.scale(scale, scale, 1.0f); - gWindow->glDrawTexture(icon, wTransform, attrib, mask); + gWindow->glDrawTexture(texture, wTransform, attrib, mask); } } } -void -UnityWindow::RenderText(UnityWindow::CairoContext const& context, - float x, float y, - float maxWidth, float maxHeight) +void UnityWindow::RenderDecoration(CairoContext const& context, double aspect) +{ + cairo_save(context.cr_); + + // Draw window decoration based on gtk style + cairo_push_group(context.cr_); + auto& style = panel::Style::Instance(); + gtk_render_background(style.GetStyleContext(), context.cr_, 0, 0, context.w_, context.h_); + gtk_render_frame(style.GetStyleContext(), context.cr_, 0, 0, context.w_, context.h_); + cairo_pop_group_to_source(context.cr_); + + // Round window decoration top border + const double radius = scale::decoration::RADIUS * aspect; + + cairo_new_sub_path(context.cr_); + cairo_line_to(context.cr_, 0, context.h_); + cairo_arc(context.cr_, radius, radius, radius, M_PI, -M_PI * 0.5f); + cairo_line_to(context.cr_, context.w_ - radius, 0); + cairo_arc(context.cr_, context.w_ - radius, radius, radius, M_PI * 0.5f, 0); + cairo_line_to(context.cr_, context.w_, context.h_); + cairo_close_path(context.cr_); + + cairo_fill(context.cr_); + + cairo_restore(context.cr_); +} + +void UnityWindow::RenderText(CairoContext const& context, int x, int y, int width, int height) { panel::Style& style = panel::Style::Instance(); - std::string fontDescription(style.GetFontDescription(panel::PanelItem::TITLE)); + std::string const& font_desc = style.GetFontDescription(panel::PanelItem::TITLE); glib::Object<PangoLayout> layout(pango_cairo_create_layout(context.cr_)); - std::shared_ptr<PangoFontDescription> font(pango_font_description_from_string(fontDescription.c_str()), + std::shared_ptr<PangoFontDescription> font(pango_font_description_from_string(font_desc.c_str()), pango_font_description_free); pango_layout_set_font_description(layout, font.get()); - GdkScreen* gdkScreen = gdk_screen_get_default(); - PangoContext* pCxt = pango_layout_get_context(layout); + GdkScreen* gdk_screen = gdk_screen_get_default(); + PangoContext* pango_ctx = pango_layout_get_context(layout); int dpi = style.GetTextDPI(); - pango_cairo_context_set_font_options(pCxt, gdk_screen_get_font_options(gdkScreen)); - pango_cairo_context_set_resolution(pCxt, dpi / static_cast<float>(PANGO_SCALE)); + pango_cairo_context_set_font_options(pango_ctx, gdk_screen_get_font_options(gdk_screen)); + pango_cairo_context_set_resolution(pango_ctx, dpi / static_cast<float>(PANGO_SCALE)); pango_layout_context_changed(layout); - pango_layout_set_height(layout, maxHeight); + decoration_title_ = WindowManager::Default()->GetWindowName(window->id()); + pango_layout_set_height(layout, height); pango_layout_set_width(layout, -1); //avoid wrap lines pango_layout_set_auto_dir(layout, false); - pango_layout_set_text(layout, - WindowManager::Default()->GetWindowName(window->id()).c_str(), - -1); + pango_layout_set_text(layout, decoration_title_.c_str(), -1); /* update the size of the pango layout */ pango_cairo_update_layout(context.cr_, layout); - cairo_set_operator(context.cr_, CAIRO_OPERATOR_OVER); - cairo_set_source_rgba(context.cr_, - 1.0, - 1.0, - 1.0, - 1.0); - // alignment - PangoRectangle lRect; - int textWidth, textHeight; + GtkStyleContext* style_context = style.GetStyleContext(); + gtk_style_context_save(style_context); - pango_layout_get_extents(layout, NULL, &lRect); - textWidth = lRect.width / PANGO_SCALE; - textHeight = lRect.height / PANGO_SCALE; + std::shared_ptr<GtkWidgetPath> widget_path(gtk_widget_path_new(), gtk_widget_path_free); + gtk_widget_path_append_type(widget_path.get(), GTK_TYPE_MENU_BAR); + gtk_widget_path_append_type(widget_path.get(), GTK_TYPE_MENU_ITEM); + gtk_widget_path_iter_set_name(widget_path.get(), -1 , "UnityPanelWidget"); - y = ((maxHeight - textHeight) / 2.0) + y; - cairo_translate(context.cr_, x, y); + gtk_style_context_set_path(style_context, widget_path.get()); + gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_MENUBAR); + gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_MENUITEM); - if (textWidth > maxWidth) + // alignment + PangoRectangle lRect; + pango_layout_get_extents(layout, nullptr, &lRect); + int text_width = lRect.width / PANGO_SCALE; + int text_height = lRect.height / PANGO_SCALE; + int text_space = width - x; + y += (height - text_height) / 2.0f; + + if (text_width > text_space) { - // apply a fade effect in the right corner - const int outPixels = textWidth - maxWidth; - const int fadingPixels = 35; - const int fadingWidth = outPixels < fadingPixels ? outPixels : fadingPixels; + // Cut the text with fade + int out_pixels = text_width - text_space; + const int fading_pixels = 35; + int fading_width = (out_pixels < fading_pixels) ? out_pixels : fading_pixels; cairo_push_group(context.cr_); - pango_cairo_show_layout(context.cr_, layout); + gtk_render_layout(style_context, context.cr_, x, y, layout); cairo_pop_group_to_source(context.cr_); - std::shared_ptr<cairo_pattern_t> linpat(cairo_pattern_create_linear(maxWidth - fadingWidth, - y, maxWidth, y), + std::shared_ptr<cairo_pattern_t> linpat(cairo_pattern_create_linear(width - fading_width, y, width, y), cairo_pattern_destroy); cairo_pattern_add_color_stop_rgba(linpat.get(), 0, 0, 0, 0, 1); cairo_pattern_add_color_stop_rgba(linpat.get(), 1, 0, 0, 0, 0); @@ -3566,61 +3644,25 @@ UnityWindow::RenderText(UnityWindow::CairoContext const& context, } else { - pango_cairo_show_layout(context.cr_, layout); + gtk_render_layout(style_context, context.cr_, x, y, layout); } + + gtk_style_context_restore(style_context); } -void -UnityWindow::DrawWindowDecoration(GLWindowPaintAttrib const& attrib, - GLMatrix const& transform, - unsigned int mask, - bool highlighted, - int x, int y, unsigned width, unsigned height) +void UnityWindow::BuildDecorationTexture() { - // Paint a fake window decoration - CairoContext context(width, height); - - cairo_save(context.cr_); - cairo_push_group(context.cr_); - - // Round window decoration top border - const double aspect = 1.0; - const double corner_radius = height / 10.0; - const double radius = corner_radius / aspect; - const double degrees = M_PI / 180.0; - - cairo_new_sub_path(context.cr_); - - cairo_arc(context.cr_, radius, radius, radius, 180 * degrees, 270 * degrees); - cairo_arc(context.cr_, width - radius, radius, radius, -90 * degrees, 0 * degrees); - cairo_line_to(context.cr_, width, height); - cairo_line_to(context.cr_, 0, height); - - cairo_close_path(context.cr_); - cairo_clip(context.cr_); - - // Draw window decoration based on gtk style - auto& style = panel::Style::Instance(); - gtk_render_background(style.GetStyleContext(), context.cr_, 0, 0, width, height); - gtk_render_frame(style.GetStyleContext(), context.cr_, 0, 0, width, height); - - cairo_pop_group_to_source(context.cr_); + if (!decoration_tex_.empty()) + return; - cairo_paint_with_alpha(context.cr_, 1.0); - cairo_restore(context.cr_); + auto const& border_extents = window->border(); - if (highlighted) + if (WindowManager::Default()->IsWindowDecorated(window->id()) && border_extents.top > 0) { - // Draw windows title - const float xText = SCALE_ITEMS_PADDING * 2 + SCALE_CLOSE_ICON_SIZE; - RenderText(context, xText, 0.0, width - xText - SCALE_ITEMS_PADDING, height); + CairoContext context(window->borderRect().width(), border_extents.top); + RenderDecoration(context); + decoration_tex_ = context.texture_; } - - mask |= PAINT_WINDOW_BLEND_MASK; - int maxWidth, maxHeight; - - for (GLTexture *icon : context.texture_) - DrawTexture(icon, attrib, transform, mask, x, y, maxWidth , maxHeight); } void UnityWindow::LoadCloseIcon(panel::WindowState state, GLTexture::List& texture) @@ -3631,12 +3673,12 @@ void UnityWindow::LoadCloseIcon(panel::WindowState state, GLTexture::List& textu auto& style = panel::Style::Instance(); auto const& files = style.GetWindowButtonFileNames(panel::WindowButtonType::CLOSE, state); - CompString pName("unityshell"); + CompString plugin("unityshell"); for (std::string const& file : files) { - CompString fileName(file.c_str()); - CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE); - texture = GLTexture::readImageToTexture(fileName, pName, size); + CompString file_name = file; + CompSize size(scale::decoration::CLOSE_SIZE, scale::decoration::CLOSE_SIZE); + texture = GLTexture::readImageToTexture(file_name, plugin, size); if (!texture.empty()) break; } @@ -3649,95 +3691,140 @@ void UnityWindow::LoadCloseIcon(panel::WindowState state, GLTexture::List& textu else if (state == panel::WindowState::PRESSED) suffix = "_pressed"; - CompString fileName((PKGDATADIR"/close_dash" + suffix + ".png").c_str()); - CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE); - texture = GLTexture::readImageToTexture(fileName, pName, size); + CompString file_name(PKGDATADIR"/close_dash" + suffix + ".png"); + CompSize size(scale::decoration::CLOSE_SIZE, scale::decoration::CLOSE_SIZE); + texture = GLTexture::readImageToTexture(file_name, plugin, size); } } -void UnityWindow::SetupScaleHeaderStyle() +void UnityWindow::SetupSharedTextures() { LoadCloseIcon(panel::WindowState::NORMAL, close_normal_tex_); LoadCloseIcon(panel::WindowState::PRELIGHT, close_prelight_tex_); LoadCloseIcon(panel::WindowState::PRESSED, close_pressed_tex_); } +void UnityWindow::CleanupSharedTextures() +{ + close_normal_tex_.clear(); + close_prelight_tex_.clear(); + close_pressed_tex_.clear(); +} + +void UnityWindow::CleanupCachedTextures() +{ + decoration_tex_.clear(); + decoration_selected_tex_.clear(); + decoration_title_.clear(); +} + void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib, GLMatrix const& transform, CompRegion const& region, unsigned int mask) { ScaleWindow *scale_win = ScaleWindow::get(window); - if (!scale_win) - return; - scale_win->scalePaintDecoration(attrib, transform, region, mask); if (!scale_win->hasSlot()) // animation not finished return; ScaleScreen* ss = ScaleScreen::get(screen); - const bool highlighted = (ss->getSelectedWindow() == window->id()); + auto state = ss->getState(); - ScalePosition const& pos = scale_win->getCurrentPosition(); - auto const& border_rect = window->borderRect(); - auto const& deco_ext = window->border(); + if (state != ScaleScreen::Wait && state != ScaleScreen::Out) + return; - const unsigned decoration_height = deco_ext.top; - unsigned width = (border_rect.width() + deco_ext.left + deco_ext.right) * pos.scale; - unsigned height = decoration_height * pos.scale; - int x = pos.x() + border_rect.x(); - int y = pos.y() + border_rect.y() + decoration_height - height - 1; + auto const& scaled_geo = GetScaledGeometry(); + auto const& pos = scale_win->getCurrentPosition(); - // If window is highlighted, we draw the decoration at full size - if (highlighted) - height = decoration_height; + const bool highlighted = (ss->getSelectedWindow() == window->id()); + int x = scaled_geo.x; + int y = scaled_geo.y; - DrawWindowDecoration(attrib, transform, mask, highlighted, x, y, width, height); + mask |= PAINT_WINDOW_BLEND_MASK; - if (highlighted) + if (!highlighted) { - x += SCALE_ITEMS_PADDING; - y += (height - SCALE_CLOSE_ICON_SIZE) / 2.0f; - int max_height = 0; - int max_width = 0; - mask |= PAINT_WINDOW_BLEND_MASK; + BuildDecorationTexture(); + DrawTexture(decoration_tex_, attrib, transform, mask, x, y, pos.scale); + close_button_geo_.Set(0, 0, 0, 0); + } + else + { + auto const& decoration_extents = window->border(); + int width = scaled_geo.width; + int height = decoration_extents.top; + bool redraw_decoration = true; + + if (!decoration_selected_tex_.empty()) + { + GLTexture* texture = decoration_selected_tex_.front(); + + if (texture->width() == width && texture->height() == height) + { + if (decoration_title_ == WindowManager::Default()->GetWindowName(window->id())) + redraw_decoration = false; + } + } + + if (redraw_decoration) + { + CairoContext context(width, height); + RenderDecoration(context, pos.scale); - switch(close_icon_state_) + // Draw window title + int text_x = scale::decoration::ITEMS_PADDING * 2 + scale::decoration::CLOSE_SIZE; + RenderText(context, text_x, 0.0, width - scale::decoration::ITEMS_PADDING, height); + decoration_selected_tex_ = context.texture_; + } + + DrawTexture(decoration_selected_tex_, attrib, transform, mask, x, y); + + x += scale::decoration::ITEMS_PADDING; + y += (height - scale::decoration::CLOSE_SIZE) / 2.0f; + + switch (close_icon_state_) { case panel::WindowState::NORMAL: default: - for (GLTexture *icon : close_normal_tex_) - DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height); + DrawTexture(close_normal_tex_, attrib, transform, mask, x, y); break; case panel::WindowState::PRELIGHT: - for (GLTexture *icon : close_prelight_tex_) - DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height); + DrawTexture(close_prelight_tex_, attrib, transform, mask, x, y); break; case panel::WindowState::PRESSED: - for (GLTexture *icon : close_pressed_tex_) - DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height); + DrawTexture(close_pressed_tex_, attrib, transform, mask, x, y); break; } - close_button_geo_.Set(x, y, max_height, max_width); - } - else if (!close_button_geo_.IsNull()) - { - close_button_geo_.Set(0, 0, 0, 0); + close_button_geo_.Set(x, y, scale::decoration::CLOSE_SIZE, scale::decoration::CLOSE_SIZE); } } -void UnityWindow::OnInitiateSpreed() +nux::Geometry UnityWindow::GetScaledGeometry() { - auto const& windows = screen->windows(); - if (std::find(windows.begin(), windows.end(), window) == windows.end()) - return; + ScaleWindow *scale_win = ScaleWindow::get(window); + + ScalePosition const& pos = scale_win->getCurrentPosition(); + auto const& border_rect = window->borderRect(); + auto const& deco_ext = window->border(); + + const unsigned width = std::floor(border_rect.width() * pos.scale); + const unsigned height = std::floor(border_rect.height() * pos.scale); + const int x = pos.x() + window->x() - std::floor(deco_ext.left * pos.scale); + const int y = pos.y() + window->y() - std::floor(deco_ext.top * pos.scale); + return nux::Geometry(x, y, width, height); +} + +void UnityWindow::OnInitiateSpread() +{ close_icon_state_ = panel::WindowState::NORMAL; - SetupScaleHeaderStyle(); + middle_clicked_ = false; + SetupSharedTextures(); WindowManager *wm = WindowManager::Default(); Window xid = window->id(); @@ -3746,17 +3833,15 @@ void UnityWindow::OnInitiateSpreed() wm->Decorate(xid); } -void UnityWindow::OnTerminateSpreed() +void UnityWindow::OnTerminateSpread() { - auto const& windows = screen->windows(); - if (std::find(windows.begin(), windows.end(), window) == windows.end()) - return; - WindowManager *wm = WindowManager::Default(); Window xid = window->id(); if (wm->IsWindowDecorated(xid) && wm->IsWindowMaximized(xid)) wm->Undecorate(xid); + + CleanupCachedTextures(); } UnityWindow::~UnityWindow() @@ -3813,6 +3898,33 @@ bool UnityPluginVTable::init() return true; } +namespace debug +{ + +ScreenIntrospection::ScreenIntrospection(CompScreen* screen) + : screen_(screen) +{} + +std::string ScreenIntrospection::GetName() const +{ + return "Screen"; +} + +void ScreenIntrospection::AddProperties(GVariantBuilder* builder) +{} + +Introspectable::IntrospectableList ScreenIntrospection::GetIntrospectableChildren() +{ + IntrospectableList children; + + for (auto const& win : screen_->windows()) + children.push_back(UnityWindow::get(win)); + + return children; +} + +} // debug namespace + namespace { diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index f9cc1a065..a11ce4dd2 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -27,7 +27,6 @@ #include <Nux/WindowThread.h> #include <NuxCore/Property.h> #include <sigc++/sigc++.h> -#include <boost/shared_ptr.hpp> #include <scale/scale.h> #include <core/core.h> @@ -50,6 +49,7 @@ #include "PanelStyle.h" #include "UScreen.h" #include "DebugDBusInterface.h" +#include "ScreenIntrospection.h" #include "SwitcherController.h" #include "UBusWrapper.h" #include "UnityshellPrivate.h" @@ -69,7 +69,7 @@ namespace unity /* base screen class */ class UnityScreen : - public unity::debug::Introspectable, + public debug::Introspectable, public sigc::trackable, public ScreenInterface, public CompositeScreenInterface, @@ -307,7 +307,7 @@ private: nux::Property<nux::Geometry> primary_monitor_; - BGHash* _bghash; + std::unique_ptr<BGHash> _bghash; ::GLFramebufferObject *oldFbo; @@ -329,11 +329,13 @@ private: nux::ObjectPtr<nux::IOpenGLBaseTexture> panel_texture_; bool scale_just_activated_; + WindowMinimizeSpeedController minimize_speed_controller_; + + debug::ScreenIntrospection screen_introspection_; UBusManager ubus_manager_; glib::SourceManager sources_; - WindowMinimizeSpeedController* minimize_speed_controller; friend class UnityWindow; }; @@ -345,6 +347,7 @@ class UnityWindow : public WrapableHandler<ScaleWindowInterface, 4>, public BaseSwitchWindow, public PluginClassHandler <UnityWindow, CompWindow>, + public debug::Introspectable, public sigc::trackable { public: @@ -356,34 +359,22 @@ public: nux::Geometry last_bound; - void minimize (); - void unminimize (); - bool minimized (); - bool focus (); - void activate (); + void minimize(); + void unminimize(); + bool minimized(); + bool focus(); + void activate(); - void updateFrameRegion (CompRegion ®ion); + void updateFrameRegion(CompRegion ®ion); /* occlusion detection * and window hiding */ - bool glPaint(const GLWindowPaintAttrib& attrib, - const GLMatrix& matrix, - const CompRegion& region, - unsigned int mask); + bool glPaint(GLWindowPaintAttrib const&, GLMatrix const&, CompRegion const&, unsigned mask); /* basic window draw function */ - bool glDraw(const GLMatrix& matrix, - const GLWindowPaintAttrib& attrib, - const CompRegion& region, - unsigned intmask); - - void updateIconPos (int &wx, - int &wy, - int x, - int y, - float width, - float height); + bool glDraw(GLMatrix const&, GLWindowPaintAttrib const&, CompRegion const&, unsigned mask); + void updateIconPos (int &wx, int &wy, int x, int y, float width, float height); void windowNotify(CompWindowNotify n); void moveNotify(int x, int y, bool immediate); void resizeNotify(int x, int y, int w, int h); @@ -391,29 +382,28 @@ public: bool place(CompPoint& pos); CompPoint tryNotIntersectUI(CompPoint& pos); + nux::Geometry GetScaledGeometry(); - void paintThumbnail (nux::Geometry const& bounding, float alpha); + void paintThumbnail(nux::Geometry const& bounding, float alpha); - void enterShowDesktop (); - void leaveShowDesktop (); - bool HandleAnimations (unsigned int ms); + void enterShowDesktop(); + void leaveShowDesktop(); + bool HandleAnimations(unsigned int ms); bool handleEvent(XEvent *event); - - typedef compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow> - UnityMinimizedHandler; - std::unique_ptr <UnityMinimizedHandler> mMinimizeHandler; - std::unique_ptr <ShowdesktopHandler> mShowdesktopHandler; + void scalePaintDecoration(GLWindowPaintAttrib const&, GLMatrix const&, CompRegion const&, unsigned mask); //! Emited when CompWindowNotifyBeforeDestroy is received sigc::signal<void> being_destroyed; - void scalePaintDecoration(const GLWindowPaintAttrib &, - const GLMatrix &, - const CompRegion &, - unsigned int); + +protected: + std::string GetName() const; + void AddProperties(GVariantBuilder* builder); private: + typedef compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow> + UnityMinimizedHandler; struct CairoContext; void DoEnableFocus (); @@ -435,8 +425,8 @@ private: void DoShow (); void DoNotifyShown (); - void OnInitiateSpreed(); - void OnTerminateSpreed(); + void OnInitiateSpread(); + void OnTerminateSpread(); void DoAddDamage (); ShowdesktopHandlerWindowInterface::PostPaintAction DoHandleAnimations (unsigned int ms); @@ -449,31 +439,35 @@ private: compiz::WindowInputRemoverLock::Ptr GetInputRemover (); - void DrawWindowDecoration(GLWindowPaintAttrib const& attrib, - GLMatrix const& transform, - unsigned int mask, - bool highlighted, - int x, int y, unsigned width, unsigned height); - void DrawTexture(GLTexture *icon, - const GLWindowPaintAttrib& attrib, - const GLMatrix& transform, - unsigned int mask, - float x, float y, - int &maxWidth, int &maxHeight); - void RenderText(CairoContext const& context, - float x, float y, - float maxWidth, float maxHeight); - - void SetupScaleHeaderStyle(); - void LoadCloseIcon(panel::WindowState state, GLTexture::List& texture); + void RenderDecoration(CairoContext const&, double aspect = 1.0f); + void RenderText(CairoContext const&, int x, int y, int width, int height); + void DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const&, + GLMatrix const&, unsigned mask, int x, int y, double scale = 1.0f); + + void BuildDecorationTexture(); + void CleanupCachedTextures(); + static void SetupSharedTextures(); + static void CleanupSharedTextures(); + static void LoadCloseIcon(panel::WindowState state, GLTexture::List& texture); +public: + std::unique_ptr <UnityMinimizedHandler> mMinimizeHandler; + +private: + std::unique_ptr <ShowdesktopHandler> mShowdesktopHandler; static GLTexture::List close_normal_tex_; static GLTexture::List close_prelight_tex_; static GLTexture::List close_pressed_tex_; + GLTexture::List decoration_tex_; + GLTexture::List decoration_selected_tex_; + std::string decoration_title_; compiz::WindowInputRemoverLock::Weak input_remover_; panel::WindowState close_icon_state_; nux::Geometry close_button_geo_; + bool middle_clicked_; glib::Source::UniquePtr focus_desktop_timeout_; + + friend class UnityScreen; }; diff --git a/tests/autopilot/unity/emulators/__init__.py b/tests/autopilot/unity/emulators/__init__.py index 48f4966a5..e0de85106 100644 --- a/tests/autopilot/unity/emulators/__init__.py +++ b/tests/autopilot/unity/emulators/__init__.py @@ -9,6 +9,7 @@ """A collection of Unity-specific emulators.""" +from time import sleep from autopilot.introspection.dbus import DBusIntrospectionObject @@ -17,3 +18,24 @@ class UnityIntrospectionObject(DBusIntrospectionObject): DBUS_SERVICE = "com.canonical.Unity" DBUS_OBJECT = "/com/canonical/Unity/Debug" + + +def ensure_unity_is_running(timeout=300): + """Poll the unity debug interface, and return when it's ready for use. + + The default timeout is 300 seconds (5 minutes) to account for the case where + Unity has crashed and is taking a while to get restarted (on a slow VM for + example). + + If, after the timeout period, unity is still not up, this function raises a + RuntimeError exception. + + """ + sleep_period=10 + for i in range(0, timeout, sleep_period): + try: + UnityIntrospectionObject.get_state_by_path("/") + return True + except: + sleep(sleep_period) + raise RuntimeError("Unity debug interface is down after %d seconds of polling." % (timeout)) diff --git a/tests/autopilot/unity/emulators/dash.py b/tests/autopilot/unity/emulators/dash.py index b23ddaa3a..f73eddb81 100644 --- a/tests/autopilot/unity/emulators/dash.py +++ b/tests/autopilot/unity/emulators/dash.py @@ -288,6 +288,14 @@ class Result(UnityIntrospectionObject): m.move(tx, ty) m.click(3) + def preview_key(self): + tx = self.x + (self.width / 2) + ty = self.y + (self.height / 2) + m = Mouse() + m.move(tx, ty) + + k = Keyboard() + k.press_and_release('Menu') class FilterBar(UnityIntrospectionObject): """A filterbar, as shown inside a lens.""" diff --git a/tests/autopilot/unity/emulators/icons.py b/tests/autopilot/unity/emulators/icons.py index 9931f988d..a5426a939 100644 --- a/tests/autopilot/unity/emulators/icons.py +++ b/tests/autopilot/unity/emulators/icons.py @@ -59,7 +59,8 @@ class SimpleLauncherIcon(UnityIntrospectionObject): return self.xids.contains(xid) def __repr__(self): - return "<%s id=%d>" % (self.__class__.__name__, self.id) + with self.no_automatic_refreshing(): + return "<%s id=%d>" % (self.__class__.__name__, self.id) class BFBLauncherIcon(SimpleLauncherIcon): @@ -78,10 +79,11 @@ class BamfLauncherIcon(SimpleLauncherIcon): """Represents a launcher icon with BAMF integration.""" def __repr__(self): - return "<%s %s id=%d>" % ( - self.__class__.__name__, - self.desktop_id, - self.id) + with self.no_automatic_refreshing(): + return "<%s %s id=%d>" % ( + self.__class__.__name__, + self.desktop_id, + self.id) class TrashLauncherIcon(SimpleLauncherIcon): """Represents the trash launcher icon.""" diff --git a/tests/autopilot/unity/emulators/panel.py b/tests/autopilot/unity/emulators/panel.py index 147afba88..1e0d3fb08 100644 --- a/tests/autopilot/unity/emulators/panel.py +++ b/tests/autopilot/unity/emulators/panel.py @@ -339,7 +339,8 @@ class IndicatorEntry(UnityIntrospectionObject): return (self.menu_x, self.menu_y, self.menu_width, self.menu_height) def __repr__(self): - return "<IndicatorEntry 0x%x (%s)>" % (id(self), self.label) + with self.no_automatic_refreshing(): + return "<IndicatorEntry 0x%x (%s)>" % (id(self), self.label) class Tray(UnityIntrospectionObject): diff --git a/tests/autopilot/unity/emulators/screen.py b/tests/autopilot/unity/emulators/screen.py new file mode 100644 index 000000000..9a7bd7513 --- /dev/null +++ b/tests/autopilot/unity/emulators/screen.py @@ -0,0 +1,42 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# + +from __future__ import absolute_import + +import logging +from unity.emulators import UnityIntrospectionObject +logger = logging.getLogger(__name__) + + +class Screen(UnityIntrospectionObject): + """The Screen class.""" + + @property + def windows(self): + """Return the available windows, or None.""" + return self.get_children_by_type(Window) + + @property + def scaled_windows(self): + """Return the available scaled windows, or None.""" + return self.get_children_by_type(Window, scaled=True) + + +class Window(UnityIntrospectionObject): + """An individual window.""" + + @property + def geometry(self): + """Returns a tuple of (x,y,w,h) for the current window.""" + return (self.x, self.y, self.width, self.height) + + @property + def scale_close_geometry(self): + """Returns a tuple of (x,y,w,h) for the scale close button.""" + return (self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height) diff --git a/tests/autopilot/unity/tests/__init__.py b/tests/autopilot/unity/tests/__init__.py index 90b7c10ae..0c46199ad 100644 --- a/tests/autopilot/unity/tests/__init__.py +++ b/tests/autopilot/unity/tests/__init__.py @@ -20,6 +20,8 @@ from tempfile import mktemp from testtools.content import text_content from testtools.matchers import Equals +from unity.emulators import ensure_unity_is_running +from unity.emulators.screen import Screen from unity.emulators.dash import Dash from unity.emulators.hud import Hud from unity.emulators.launcher import LauncherController @@ -45,6 +47,8 @@ class UnityTestCase(AutopilotTestCase): def setUp(self): super(UnityTestCase, self).setUp() + ensure_unity_is_running() + self._setUpUnityLogging() self._initial_workspace_num = self.workspace.current_workspace self.addCleanup(self.check_test_behavior) @@ -114,6 +118,12 @@ class UnityTestCase(AutopilotTestCase): return True @property + def screen(self): + if not getattr(self, '__screen', None): + self.__screen = self._get_screen() + return self.__screen + + @property def dash(self): if not getattr(self, '__dash', None): self.__dash = Dash() @@ -155,6 +165,11 @@ class UnityTestCase(AutopilotTestCase): self.__workspace = WorkspaceManager() return self.__workspace + def _get_screen(self): + screens = Screen.get_all_instances() + self.assertThat(len(screens), Equals(1)) + return screens[0] + def _get_launcher_controller(self): controllers = LauncherController.get_all_instances() self.assertThat(len(controllers), Equals(1)) diff --git a/tests/autopilot/unity/tests/launcher/test_icon_behavior.py b/tests/autopilot/unity/tests/launcher/test_icon_behavior.py index 41917033c..09c7c3064 100644 --- a/tests/autopilot/unity/tests/launcher/test_icon_behavior.py +++ b/tests/autopilot/unity/tests/launcher/test_icon_behavior.py @@ -19,9 +19,6 @@ from unity.emulators.icons import BamfLauncherIcon from unity.emulators.launcher import IconDragType from unity.tests.launcher import LauncherTestCase, _make_scenarios -import pygtk -import gtk - logger = logging.getLogger(__name__) @@ -117,30 +114,6 @@ class LauncherIconsTests(LauncherTestCase): self.assertThat(self.window_manager.scale_active, Eventually(Equals(True))) self.assertThat(self.window_manager.scale_active_for_group, Eventually(Equals(True))) - def test_clicking_icon_twice_with_override_redirect_window_opens_spread(self): - """This tests shows that when you click on a launcher icon twice and a - window exist that has override direct set then it must ignore that window - and initate spread. - """ - # Opens an override redirect window - popup = gtk.Window(gtk.WINDOW_POPUP) - popup.show_all() - self.addCleanup(popup.destroy) - - char_win1 = self.start_app_window("Character Map") - char_win2 = self.start_app_window("Character Map") - char_app = char_win1.application - - self.assertVisibleWindowStack([char_win2, char_win1]) - self.assertProperty(char_win2, is_focused=True) - - char_icon = self.launcher.model.get_icon(desktop_id=char_app.desktop_file) - self.addCleanup(self.keybinding, "spread/cancel") - self.launcher_instance.click_launcher_icon(char_icon) - - self.assertThat(self.window_manager.scale_active, Eventually(Equals(True))) - self.assertThat(self.window_manager.scale_active_for_group, Eventually(Equals(True))) - def test_while_in_scale_mode_the_dash_will_still_open(self): """If scale is initiated through the laucher pressing super must close scale and open the dash. diff --git a/tests/autopilot/unity/tests/test_dash.py b/tests/autopilot/unity/tests/test_dash.py index 2c872c2c4..254965dc6 100644 --- a/tests/autopilot/unity/tests/test_dash.py +++ b/tests/autopilot/unity/tests/test_dash.py @@ -115,10 +115,10 @@ class DashRevealTests(DashTestCase): (x,y,w,h) = self.dash.geometry (screen_x,screen_y,screen_w,screen_h) = self.screen_geo.get_monitor_geometry(current_monitor) - + self.mouse.move(x + w + (screen_w-((screen_x-x)+w))/2, y + h + (screen_h-((screen_y-y)+h))/2) self.mouse.click() - + self.assertThat(self.dash.visible, Eventually(Equals(False))) def test_closes_then_focuses_window_on_mouse_down(self): @@ -660,9 +660,7 @@ class CategoryHeaderTests(DashTestCase): class PreviewInvocationTests(DashTestCase): """Tests that dash previews can be opened and closed in different lenses. - """ - def test_app_lens_preview_open_close(self): """Right-clicking on an application lens result must show its preview. @@ -685,7 +683,6 @@ class PreviewInvocationTests(DashTestCase): def test_files_lens_preview_open_close(self): """Right-clicking on a files lens result must show its preview. - """ lens = self.dash.reveal_file_lens() self.addCleanup(self.dash.ensure_hidden) @@ -704,7 +701,6 @@ class PreviewInvocationTests(DashTestCase): def test_music_lens_preview_open_close(self): """Right-clicking on a music lens result must show its preview. - """ lens = self.dash.reveal_music_lens() self.addCleanup(self.dash.ensure_hidden) @@ -729,8 +725,8 @@ class PreviewInvocationTests(DashTestCase): def test_video_lens_preview_open_close(self): """Right-clicking on a video lens result must show its preview. - """ + def get_category(lens): category = lens.get_category_by_name("Recently Viewed") # If there was no video played on this system this category is expected @@ -759,6 +755,20 @@ class PreviewInvocationTests(DashTestCase): self.assertThat(self.dash.preview_displaying, Eventually(Equals(False))) + def test_preview_key(self): + """Pressing menu key on a selected dash result must show + its preview. + """ + lens = self.dash.reveal_application_lens() + self.addCleanup(self.dash.ensure_hidden) + + category = lens.get_category_by_name("Installed") + results = category.get_results() + result = results[0] + # result.preview_key() handles finding xy co-ords and key press + result.preview_key() + self.assertThat(self.dash.preview_displaying, Eventually(Equals(True))) + class PreviewNavigateTests(DashTestCase): """Tests that mouse navigation works with previews.""" diff --git a/tests/autopilot/unity/tests/test_shopping_lens.py b/tests/autopilot/unity/tests/test_shopping_lens.py new file mode 100644 index 000000000..bc0ee0d48 --- /dev/null +++ b/tests/autopilot/unity/tests/test_shopping_lens.py @@ -0,0 +1,112 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Brandon Schaefer +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from testtools.matchers import Equals, GreaterThan +from time import sleep +import urllib2 + +from unity.tests import UnityTestCase + + +class ShoppingLensTests(UnityTestCase): + """Test the shopping lens bahavior.""" + + def setUp(self): + super(ShoppingLensTests, self).setUp() + try: + urllib2.urlopen("http://www.google.com", timeout=2) + except urllib2.URLError, e: + self.skip("Skipping test, no internet connection") + + def tearDown(self): + self.dash.ensure_hidden() + super(ShoppingLensTests, self).tearDown() + + def test_no_results_in_home_lens_if_empty_search(self): + """Test that the home lens contains no results if the search bar is empty.""" + self.dash.ensure_visible() + lens = self.dash.get_current_lens() + + results_category = lens.get_category_by_name("More suggestions") + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(Equals(0))) + + def test_home_lens_has_shopping_results(self): + """Test that the home lens contains results.""" + self.dash.ensure_visible() + lens = self.dash.get_current_lens() + + self.keyboard.type("playstation") + results_category = lens.get_category_by_name("More suggestions") + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1))) + + def test_application_lens_has_shopping_results(self): + """Test that the application lens contains results.""" + self.dash.reveal_application_lens() + lens = self.dash.get_current_lens() + + self.keyboard.type("Text Editor") + results_category = lens.get_category_by_name("More suggestions") + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1))) + + def test_music_lens_has_shopping_results(self): + """Test that the music lens contains results.""" + self.dash.reveal_music_lens() + lens = self.dash.get_current_lens() + + self.keyboard.type("megadeth") + results_category = lens.get_category_by_name("More suggestions") + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1))) + + def test_preview_works_with_shopping_lens(self): + """This test shows the dash preview works with shopping lens results.""" + self.dash.ensure_visible() + lens = self.dash.get_current_lens() + + self.keyboard.type("a") + results_category = lens.get_category_by_name("More suggestions") + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(1))) + + results = results_category.get_results() + results[0].preview() + + self.assertThat(self.dash.preview_displaying, Eventually(Equals(True))) + + def test_shopping_lens_preview_navigate_right(self): + """This test shows that shopping lens results can open previews, + then move to the next shopping result. + """ + self.dash.ensure_visible() + lens = self.dash.get_current_lens() + + self.keyboard.type("a") + results_category = lens.get_category_by_name("More suggestions") + + refresh_results_fn = lambda: len(results_category.get_results()) + self.assertThat(refresh_results_fn, Eventually(GreaterThan(2))) + + results = results_category.get_results() + results[0].preview() + + self.assertThat(self.dash.preview_displaying, Eventually(Equals(True))) + self.preview_container = self.dash.view.get_preview_container() + start_index = self.preview_container.relative_nav_index + self.preview_container.navigate_right() + + self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(start_index+1))) diff --git a/tests/autopilot/unity/tests/test_spread.py b/tests/autopilot/unity/tests/test_spread.py new file mode 100644 index 000000000..f1e3fc3f0 --- /dev/null +++ b/tests/autopilot/unity/tests/test_spread.py @@ -0,0 +1,124 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Marco Trevisan (Treviño) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +import logging +from time import sleep +from testtools.matchers import Equals, NotEquals + +from unity.tests import UnityTestCase +from unity.emulators.screen import Screen + + +class SpreadTests(UnityTestCase): + """Spread tests""" + + def start_test_application_windows(self, app_name, num_windows=2): + """Start a given number of windows of the requested application""" + self.close_all_app(app_name) + windows = [] + + for i in range(num_windows): + win = self.start_app_window(app_name) + if len(windows): + self.assertThat(win.application, Equals(windows[-1].application)) + + windows.append(win) + + self.assertThat(len(windows), Equals(num_windows)) + + return windows + + def initiate_spread_for_screen(self): + """Initiate the Spread for all windows""" + self.addCleanup(self.keybinding, "spread/cancel") + self.keybinding("spread/start") + sleep(1) + self.assertThat(self.window_manager.scale_active, Eventually(Equals(True))) + + + def initiate_spread_for_application(self, desktop_id): + """Initiate the Spread for windows of the given app""" + icon = self.launcher.model.get_icon(desktop_id=desktop_id) + self.assertThat(lambda: icon, Eventually(NotEquals(None))) + launcher = self.launcher.get_launcher_for_monitor(self.screen_geo.get_primary_monitor()) + + self.addCleanup(self.keybinding, "spread/cancel") + launcher.click_launcher_icon(icon) + self.assertThat(self.window_manager.scale_active_for_group, Eventually(Equals(True))) + + def assertWindowIsNotScaled(self, xid): + """Assert that a window is not scaled""" + refresh_fn = lambda: xid in [w.xid for w in self.screen.scaled_windows] + self.assertThat(refresh_fn, Eventually(Equals(False))) + + def assertWindowIsClosed(self, xid): + """Assert that a window is not in the list of the open windows""" + refresh_fn = lambda: xid in [w.x_id for w in self.bamf.get_open_windows()] + self.assertThat(refresh_fn, Eventually(Equals(False))) + + + def test_scale_application_windows(self): + """Test if all the windows of an application are scaled when application spread is initiated""" + [win1, win2] = self.start_test_application_windows("Calculator") + self.initiate_spread_for_application(win1.application.desktop_file) + + self.assertThat(lambda: len(self.screen.scaled_windows), Eventually(Equals(2))) + self.assertThat(lambda: (win1.x_id and win2.x_id) in [w.xid for w in self.screen.scaled_windows], + Eventually(Equals(True))) + + def test_scaled_window_is_focused_on_click(self): + """Test that a window is focused when clicked in spread""" + windows = self.start_test_application_windows("Calculator", 3) + self.initiate_spread_for_application(windows[0].application.desktop_file) + + not_focused = [w for w in windows if not w.is_focused][0] + + target_xid = not_focused.x_id + [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid] + + (x, y, w, h) = target_win.geometry + self.mouse.move(x + w / 2, y + h / 2) + sleep(.5) + self.mouse.click() + + self.assertThat(lambda: not_focused.is_focused, Eventually(Equals(True))) + + def test_scaled_window_closes_on_middle_click(self): + """Test that a window is closed when middle-clicked in spread""" + win = self.start_test_application_windows("Calculator", 2)[0] + self.initiate_spread_for_application(win.application.desktop_file) + + target_xid = win.x_id + [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid] + + (x, y, w, h) = target_win.geometry + self.mouse.move(x + w / 2, y + h / 2) + sleep(.5) + self.mouse.click(button=2) + + self.assertWindowIsNotScaled(target_xid) + self.assertWindowIsClosed(target_xid) + + def test_scaled_window_closes_on_close_button_click(self): + """Test that a window is closed when its close button is clicked in spread""" + win = self.start_test_application_windows("Calculator", 1)[0] + self.initiate_spread_for_screen() + + target_xid = win.x_id + [target_win] = [w for w in self.screen.scaled_windows if w.xid == target_xid] + + (x, y, w, h) = target_win.scale_close_geometry + self.mouse.move(x + w / 2, y + h / 2) + sleep(.5) + self.mouse.click() + + self.assertWindowIsNotScaled(target_xid) + self.assertWindowIsClosed(target_xid) diff --git a/unity-shared/CoverArt.cpp b/unity-shared/CoverArt.cpp index 725e038e2..5cc5e1788 100644 --- a/unity-shared/CoverArt.cpp +++ b/unity-shared/CoverArt.cpp @@ -334,7 +334,7 @@ void CoverArt::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) gfx_engine.GetRenderStates().GetBlend(alpha, src, dest); gfx_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - if (texture_screenshot_) + if (IsFullRedraw() && texture_screenshot_) { nux::Geometry imageDest = base; nux::TexCoordXForm texxform; @@ -376,7 +376,7 @@ void CoverArt::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw) texxform, nux::color::White); } - else + else if (IsFullRedraw()) { if (waiting_) { diff --git a/unity-shared/IconLoader.cpp b/unity-shared/IconLoader.cpp index b26d464e9..86b7785ae 100644 --- a/unity-shared/IconLoader.cpp +++ b/unity-shared/IconLoader.cpp @@ -533,6 +533,10 @@ private: unsigned cat_size = max_font_height * 9 / 8; switch (category) { + case UNITY_PROTOCOL_CATEGORY_TYPE_NONE: + // rest of the processing is the CategoryIconLoaded, lets invoke it + helper_slot("", -1, cat_size, glib::Object<GdkPixbuf>()); + break; case UNITY_PROTOCOL_CATEGORY_TYPE_APPLICATION: helper_handle = impl->LoadFromFilename(PKGDATADIR"/emblem_apps.svg", -1, cat_size, helper_slot); @@ -574,9 +578,8 @@ private: case UNITY_PROTOCOL_CATEGORY_TYPE_TOOLS: case UNITY_PROTOCOL_CATEGORY_TYPE_CAR: default: - // rest of the processing is the CategoryIconLoaded, lets invoke it - glib::Object<GdkPixbuf> null_pixbuf; - helper_slot("", -1, cat_size, null_pixbuf); + helper_handle = + impl->LoadFromFilename(PKGDATADIR"/emblem_others.svg", -1, cat_size, helper_slot); break; } } |
