summaryrefslogtreecommitdiff
diff options
-rw-r--r--dash/ResultViewGrid.cpp12
-rw-r--r--dash/previews/ActionButton.cpp5
-rw-r--r--launcher/BamfLauncherIcon.cpp41
-rw-r--r--manual-tests/Switcher.txt16
-rw-r--r--plugins/unityshell/resources/emblem_apps.svg109
-rw-r--r--plugins/unityshell/resources/emblem_others.svg12
-rw-r--r--plugins/unityshell/resources/emblem_video.svg75
-rw-r--r--plugins/unityshell/resources/lens-nav-gwibber.svg14
-rw-r--r--plugins/unityshell/src/ScreenIntrospection.h48
-rw-r--r--plugins/unityshell/src/unityshell.cpp494
-rw-r--r--plugins/unityshell/src/unityshell.h108
-rw-r--r--tests/autopilot/unity/emulators/__init__.py22
-rw-r--r--tests/autopilot/unity/emulators/dash.py8
-rw-r--r--tests/autopilot/unity/emulators/icons.py12
-rw-r--r--tests/autopilot/unity/emulators/panel.py3
-rw-r--r--tests/autopilot/unity/emulators/screen.py42
-rw-r--r--tests/autopilot/unity/tests/__init__.py15
-rw-r--r--tests/autopilot/unity/tests/launcher/test_icon_behavior.py27
-rw-r--r--tests/autopilot/unity/tests/test_dash.py24
-rw-r--r--tests/autopilot/unity/tests/test_shopping_lens.py112
-rw-r--r--tests/autopilot/unity/tests/test_spread.py124
-rw-r--r--unity-shared/CoverArt.cpp4
-rw-r--r--unity-shared/IconLoader.cpp9
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 &region);
+ void updateFrameRegion(CompRegion &region);
/* 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;
}
}