Merge lp:~3v1n0/unity/spread-filter into lp:unity

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Brandon Schaefer
Approved revision: no longer in the source branch.
Merged at revision: 3668
Proposed branch: lp:~3v1n0/unity/spread-filter
Merge into: lp:unity
Prerequisite: lp:~3v1n0/unity/spreadish-scale
Diff against target: 964 lines (+484/-93)
14 files modified
plugins/unityshell/src/unityshell.cpp (+60/-30)
plugins/unityshell/src/unityshell.h (+4/-0)
plugins/unityshell/unityshell.xml.in (+1/-0)
shutdown/SessionController.h (+0/-1)
tests/CMakeLists.txt (+1/-0)
tests/autopilot/unity/emulators/screen.py (+23/-0)
tests/autopilot/unity/tests/test_spread.py (+49/-8)
tests/test_spread_filter.cpp (+116/-0)
unity-shared/CMakeLists.txt (+1/-0)
unity-shared/PluginAdapter.cpp (+3/-7)
unity-shared/SearchBar.cpp (+40/-45)
unity-shared/SearchBar.h (+1/-2)
unity-shared/SpreadFilter.cpp (+121/-0)
unity-shared/SpreadFilter.h (+64/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/spread-filter
Reviewer Review Type Date Requested Status
Brandon Schaefer (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+206802@code.launchpad.net

Commit message

UnityScreen: add a SpreadFilter when in Scale mode, when updated it filters the scale results

The SpreadFilter is a BaseWindow with a SearchBar shown on the top-left corner of the
active workspace that is hidden by default monitoring key-presses; when some content is
written, the bar is shown, while is hidden when empty.

Thanks to this we can finally filter the windows by name in the unity spread!

Description of the change

As defined in the Spread design doc [1]:

Filtering Windows by Title Captions
The user can start typing to filter the windows shown to include only those which contain the search text in their title bar.

So, added a new SpreadFilter class that basically just shows a SearchBar that is hidden unless the user won't type anything useful to filter the spreaded windows.

Design: http://go.3v1n0.net/1eJfQCF
Actual implementation: http://people.ubuntu.com/~3v1n0/Spread-Filter.webm

Tests added, it has a soft-dependency on lp:~3v1n0/compiz/scale-improvements to properly work.

[1] http://go.3v1n0.net/1bFTXd2

To post a comment you must log in.
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Looks very nice :).

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'plugins/unityshell/src/unityshell.cpp'
--- plugins/unityshell/src/unityshell.cpp 2014-02-18 00:59:55 +0000
+++ plugins/unityshell/src/unityshell.cpp 2014-02-18 00:59:55 +0000
@@ -137,10 +137,8 @@
137const unsigned int SCROLL_UP_BUTTON = 7;137const unsigned int SCROLL_UP_BUTTON = 7;
138const int MAX_BUFFER_AGE = 11;138const int MAX_BUFFER_AGE = 11;
139const int FRAMES_TO_REDRAW_ON_RESUME = 10;139const int FRAMES_TO_REDRAW_ON_RESUME = 10;
140
141const std::string RELAYOUT_TIMEOUT = "relayout-timeout";140const std::string RELAYOUT_TIMEOUT = "relayout-timeout";
142} // namespace local141} // namespace local
143
144} // anon namespace142} // anon namespace
145143
146UnityScreen::UnityScreen(CompScreen* screen)144UnityScreen::UnityScreen(CompScreen* screen)
@@ -149,6 +147,7 @@
149 , screen(screen)147 , screen(screen)
150 , cScreen(CompositeScreen::get(screen))148 , cScreen(CompositeScreen::get(screen))
151 , gScreen(GLScreen::get(screen))149 , gScreen(GLScreen::get(screen))
150 , sScreen(ScaleScreen::get(screen))
152 , menus_(std::make_shared<menu::Manager>(std::make_shared<indicator::DBusIndicators>(), std::make_shared<key::GnomeGrabber>()))151 , menus_(std::make_shared<menu::Manager>(std::make_shared<indicator::DBusIndicators>(), std::make_shared<key::GnomeGrabber>()))
153 , deco_manager_(std::make_shared<decoration::Manager>())152 , deco_manager_(std::make_shared<decoration::Manager>())
154 , debugger_(this)153 , debugger_(this)
@@ -511,13 +510,28 @@
511510
512void UnityScreen::OnInitiateSpread()511void UnityScreen::OnInitiateSpread()
513{512{
514 for (auto const& swin : ScaleScreen::get(screen)->getWindows())513 spread_filter_ = std::make_shared<spread::Filter>();
514 spread_filter_->text.changed.connect([this] (std::string const& filter) {
515 if (filter.empty())
516 {
517 sScreen->relayoutSlots(CompMatch::emptyMatch);
518 }
519 else
520 {
521 auto match = sScreen->getCustomMatch();
522 sScreen->relayoutSlots(match & ("ititle="+filter));
523 }
524 });
525
526 for (auto const& swin : sScreen->getWindows())
515 UnityWindow::get(swin->window)->OnInitiateSpread();527 UnityWindow::get(swin->window)->OnInitiateSpread();
516}528}
517529
518void UnityScreen::OnTerminateSpread()530void UnityScreen::OnTerminateSpread()
519{531{
520 for (auto const& swin : ScaleScreen::get(screen)->getWindows())532 spread_filter_.reset();
533
534 for (auto const& swin : sScreen->getWindows())
521 UnityWindow::get(swin->window)->OnTerminateSpread();535 UnityWindow::get(swin->window)->OnTerminateSpread();
522}536}
523537
@@ -1680,8 +1694,7 @@
1680 case MotionNotify:1694 case MotionNotify:
1681 if (wm.IsScaleActive())1695 if (wm.IsScaleActive())
1682 {1696 {
1683 ScaleScreen* ss = ScaleScreen::get(screen);1697 if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow()))
1684 if (CompWindow *w = screen->findWindow(ss->getSelectedWindow()))
1685 skip_other_plugins = UnityWindow::get(w)->handleEvent(event);1698 skip_other_plugins = UnityWindow::get(w)->handleEvent(event);
1686 }1699 }
1687 else if (switcher_controller_->IsDetailViewShown())1700 else if (switcher_controller_->IsDetailViewShown())
@@ -1706,9 +1719,14 @@
1706 }1719 }
1707 if (wm.IsScaleActive())1720 if (wm.IsScaleActive())
1708 {1721 {
1709 ScaleScreen* ss = ScaleScreen::get(screen);1722 if (spread_filter_ && spread_filter_->Visible())
1710 if (CompWindow *w = screen->findWindow(ss->getSelectedWindow()))1723 skip_other_plugins = spread_filter_->GetAbsoluteGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root);
1711 skip_other_plugins = UnityWindow::get(w)->handleEvent(event);1724
1725 if (!skip_other_plugins)
1726 {
1727 if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow()))
1728 skip_other_plugins = UnityWindow::get(w)->handleEvent(event);
1729 }
1712 }1730 }
1713 else if (switcher_controller_->IsDetailViewShown())1731 else if (switcher_controller_->IsDetailViewShown())
1714 {1732 {
@@ -1782,9 +1800,14 @@
1782 }1800 }
1783 else if (wm.IsScaleActive())1801 else if (wm.IsScaleActive())
1784 {1802 {
1785 ScaleScreen* ss = ScaleScreen::get(screen);1803 if (spread_filter_ && spread_filter_->Visible())
1786 if (CompWindow *w = screen->findWindow(ss->getSelectedWindow()))1804 skip_other_plugins = spread_filter_->GetAbsoluteGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root);
1787 skip_other_plugins = UnityWindow::get(w)->handleEvent(event);1805
1806 if (!skip_other_plugins)
1807 {
1808 if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow()))
1809 skip_other_plugins = skip_other_plugins || UnityWindow::get(w)->handleEvent(event);
1810 }
1788 }1811 }
1789 break;1812 break;
1790 case KeyPress:1813 case KeyPress:
@@ -1853,6 +1876,16 @@
1853 EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false);1876 EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false);
1854 }1877 }
1855 }1878 }
1879
1880 if (spread_filter_ && spread_filter_->Visible())
1881 {
1882 if (key_sym == XK_Escape)
1883 {
1884 skip_other_plugins = true;
1885 spread_filter_->text = "";
1886 }
1887 }
1888
1856 break;1889 break;
1857 }1890 }
1858 case MapRequest:1891 case MapRequest:
@@ -1866,22 +1899,21 @@
1866 }1899 }
1867 break;1900 break;
1868 default:1901 default:
1869 if (screen->shapeEvent () + ShapeNotify == event->type)1902 if (screen->shapeEvent() + ShapeNotify == event->type)
1870 {1903 {
1871 Window xid = event->xany.window;1904 Window xid = event->xany.window;
1872 CompWindow *w = screen->findWindow(xid);1905 CompWindow *w = screen->findWindow(xid);
18731906
1874 if (w)1907 if (w)
1875 {1908 {
1876 UnityWindow *uw = UnityWindow::get (w);1909 UnityWindow *uw = UnityWindow::get(w);
1877
1878 uw->handleEvent(event);1910 uw->handleEvent(event);
1879 }1911 }
1880 }1912 }
1881 break;1913 break;
1882 }1914 }
18831915
1884 compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow>::handleEvent (event);1916 compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow>::handleEvent(event);
18851917
1886 // avoid further propagation (key conflict for instance)1918 // avoid further propagation (key conflict for instance)
1887 if (!skip_other_plugins)1919 if (!skip_other_plugins)
@@ -1890,19 +1922,18 @@
1890 if (deco_manager_->HandleEventAfter(event))1922 if (deco_manager_->HandleEventAfter(event))
1891 return;1923 return;
18921924
1893 switch (event->type)1925 if (event->type == MapRequest)
1894 {1926 ShowdesktopHandler::AllowLeaveShowdesktopMode(event->xmaprequest.window);
1895 case MapRequest:
1896 ShowdesktopHandler::AllowLeaveShowdesktopMode(event->xmaprequest.window);
1897 break;
1898 }
18991927
1900 if ((event->type == MotionNotify || event->type == ButtonPress || event->type == ButtonRelease) &&1928 if (switcher_controller_->IsMouseDisabled() && switcher_controller_->Visible() &&
1901 switcher_controller_->IsMouseDisabled() && switcher_controller_->Visible())1929 (event->type == MotionNotify || event->type == ButtonPress || event->type == ButtonRelease))
1902 {1930 {
1903 skip_other_plugins = true;1931 skip_other_plugins = true;
1904 }1932 }
19051933
1934 if (spread_filter_ && spread_filter_->Visible())
1935 skip_other_plugins = false;
1936
1906 if (!skip_other_plugins &&1937 if (!skip_other_plugins &&
1907 screen->otherGrabExist("deco", "move", "switcher", "resize", nullptr))1938 screen->otherGrabExist("deco", "move", "switcher", "resize", nullptr))
1908 {1939 {
@@ -2786,7 +2817,7 @@
2786 }2817 }
27872818
2788 if (WindowManager::Default().IsScaleActive() &&2819 if (WindowManager::Default().IsScaleActive() &&
2789 ScaleScreen::get(screen)->getSelectedWindow() == window->id())2820 uScreen->sScreen->getSelectedWindow() == window->id())
2790 {2821 {
2791 nux::Geometry const& scaled_geo = GetScaledGeometry();2822 nux::Geometry const& scaled_geo = GetScaledGeometry();
2792 paintInnerGlow(scaled_geo, matrix, attrib, mask);2823 paintInnerGlow(scaled_geo, matrix, attrib, mask);
@@ -3737,7 +3768,7 @@
3737void UnityWindow::AddProperties(debug::IntrospectionData& introspection)3768void UnityWindow::AddProperties(debug::IntrospectionData& introspection)
3738{3769{
3739 Window xid = window->id();3770 Window xid = window->id();
3740 auto const& swins = ScaleScreen::get(screen)->getWindows();3771 auto const& swins = uScreen->sScreen->getWindows();
3741 bool scaled = std::find(swins.begin(), swins.end(), ScaleWindow::get(window)) != swins.end();3772 bool scaled = std::find(swins.begin(), swins.end(), ScaleWindow::get(window)) != swins.end();
3742 WindowManager& wm = WindowManager::Default();3773 WindowManager& wm = WindowManager::Default();
37433774
@@ -3942,8 +3973,7 @@
3942 if (!scale_win->hasSlot()) // animation not finished3973 if (!scale_win->hasSlot()) // animation not finished
3943 return;3974 return;
39443975
3945 ScaleScreen* ss = ScaleScreen::get(screen);3976 auto state = uScreen->sScreen->getState();
3946 auto state = ss->getState();
39473977
3948 if (state != ScaleScreen::Wait && state != ScaleScreen::Out)3978 if (state != ScaleScreen::Wait && state != ScaleScreen::Out)
3949 return;3979 return;
@@ -3953,7 +3983,7 @@
3953 auto deco_attrib = attrib;3983 auto deco_attrib = attrib;
3954 deco_attrib.opacity = COMPIZ_COMPOSITE_OPAQUE;3984 deco_attrib.opacity = COMPIZ_COMPOSITE_OPAQUE;
39553985
3956 bool highlighted = (ss->getSelectedWindow() == window->id());3986 bool highlighted = (uScreen->sScreen->getSelectedWindow() == window->id());
3957 paintFakeDecoration(scale_geo, deco_attrib, transform, mask, highlighted, pos.scale);3987 paintFakeDecoration(scale_geo, deco_attrib, transform, mask, highlighted, pos.scale);
3958}3988}
39593989
@@ -4130,7 +4160,7 @@
41304160
4131Introspectable::IntrospectableList ScreenIntrospection::GetIntrospectableChildren()4161Introspectable::IntrospectableList ScreenIntrospection::GetIntrospectableChildren()
4132{4162{
4133 IntrospectableList children;4163 IntrospectableList children({uScreen->spread_filter_.get()});
41344164
4135 for (auto const& win : screen_->windows())4165 for (auto const& win : screen_->windows())
4136 children.push_back(UnityWindow::get(win));4166 children.push_back(UnityWindow::get(win));
41374167
=== modified file 'plugins/unityshell/src/unityshell.h'
--- plugins/unityshell/src/unityshell.h 2014-02-12 07:13:01 +0000
+++ plugins/unityshell/src/unityshell.h 2014-02-18 00:59:55 +0000
@@ -65,6 +65,7 @@
65#include "ScreenIntrospection.h"65#include "ScreenIntrospection.h"
66#include "SwitcherController.h"66#include "SwitcherController.h"
67#include "SessionController.h"67#include "SessionController.h"
68#include "SpreadFilter.h"
68#include "UBusWrapper.h"69#include "UBusWrapper.h"
69#include "UnityshellPrivate.h"70#include "UnityshellPrivate.h"
70#include "UnityShowdesktopHandler.h"71#include "UnityShowdesktopHandler.h"
@@ -118,6 +119,7 @@
118 CompScreen* screen;119 CompScreen* screen;
119 CompositeScreen* cScreen;120 CompositeScreen* cScreen;
120 GLScreen* gScreen;121 GLScreen* gScreen;
122 ScaleScreen* sScreen;
121123
122 /* prepares nux for drawing */124 /* prepares nux for drawing */
123 void nuxPrologue();125 void nuxPrologue();
@@ -327,6 +329,7 @@
327 session::Controller::Ptr session_controller_;329 session::Controller::Ptr session_controller_;
328 debug::DebugDBusInterface debugger_;330 debug::DebugDBusInterface debugger_;
329 std::unique_ptr<BGHash> bghash_;331 std::unique_ptr<BGHash> bghash_;
332 spread::Filter::Ptr spread_filter_;
330333
331 /* Subscription for gestures that manipulate Unity launcher */334 /* Subscription for gestures that manipulate Unity launcher */
332 std::unique_ptr<nux::GesturesSubscription> gestures_sub_launcher_;335 std::unique_ptr<nux::GesturesSubscription> gestures_sub_launcher_;
@@ -407,6 +410,7 @@
407 bool is_desktop_active_;410 bool is_desktop_active_;
408411
409 friend class UnityWindow;412 friend class UnityWindow;
413 friend class debug::ScreenIntrospection;
410 friend class decoration::Manager;414 friend class decoration::Manager;
411};415};
412416
413417
=== modified file 'plugins/unityshell/unityshell.xml.in'
--- plugins/unityshell/unityshell.xml.in 2014-02-04 16:38:45 +0000
+++ plugins/unityshell/unityshell.xml.in 2014-02-18 00:59:55 +0000
@@ -43,6 +43,7 @@
43 </requirement>43 </requirement>
44 <conflict>44 <conflict>
45 <plugin>decor</plugin>45 <plugin>decor</plugin>
46 <plugin>scalefilter</plugin>
46 </conflict>47 </conflict>
47 </deps>48 </deps>
4849
4950
=== modified file 'shutdown/SessionController.h'
--- shutdown/SessionController.h 2014-01-06 19:50:53 +0000
+++ shutdown/SessionController.h 2014-02-18 00:59:55 +0000
@@ -25,7 +25,6 @@
25#include <Nux/Nux.h>25#include <Nux/Nux.h>
26#include <Nux/BaseWindow.h>26#include <Nux/BaseWindow.h>
27#include <Nux/HLayout.h>27#include <Nux/HLayout.h>
28#include <NuxCore/Color.h>
29#include <NuxCore/Animation.h>28#include <NuxCore/Animation.h>
30#include <UnityCore/SessionManager.h>29#include <UnityCore/SessionManager.h>
3130
3231
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2014-02-07 21:50:15 +0000
+++ tests/CMakeLists.txt 2014-02-18 00:59:55 +0000
@@ -278,6 +278,7 @@
278 test_single_monitor_launcher_icon.cpp278 test_single_monitor_launcher_icon.cpp
279 test_showdesktop_handler.cpp279 test_showdesktop_handler.cpp
280 test_software_center_launcher_icon.cpp280 test_software_center_launcher_icon.cpp
281 test_spread_filter.cpp
281 test_static_cairo_text.cpp282 test_static_cairo_text.cpp
282 test_switcher_controller.cpp283 test_switcher_controller.cpp
283 test_switcher_controller_class.cpp284 test_switcher_controller_class.cpp
284285
=== modified file 'tests/autopilot/unity/emulators/screen.py'
--- tests/autopilot/unity/emulators/screen.py 2013-10-08 14:08:52 +0000
+++ tests/autopilot/unity/emulators/screen.py 2014-02-18 00:59:55 +0000
@@ -28,6 +28,15 @@
28 """Return the available scaled windows, or None."""28 """Return the available scaled windows, or None."""
29 return self.get_children_by_type(Window, scaled=True)29 return self.get_children_by_type(Window, scaled=True)
3030
31 @property
32 def spread_filter(self):
33 """Return the spread filter, or None."""
34 filter = self.get_children_by_type(SpreadFilter)
35 if len(filter):
36 return filter[0]
37
38 return None
39
31 def window(self, xid):40 def window(self, xid):
32 """Return the window with given xid."""41 """Return the window with given xid."""
33 windows = self.get_children_by_type(Window, xid=xid)42 windows = self.get_children_by_type(Window, xid=xid)
@@ -51,3 +60,17 @@
51 self.scaled_close_width.wait_for(GreaterThan(0))60 self.scaled_close_width.wait_for(GreaterThan(0))
52 self.scaled_close_height.wait_for(GreaterThan(0))61 self.scaled_close_height.wait_for(GreaterThan(0))
53 return (self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height)62 return (self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height)
63
64
65class SpreadFilter(UnityIntrospectionObject):
66 """The spread filter."""
67
68 @property
69 def search_bar(self):
70 """Return the search bar."""
71 [search_bar] = self.get_children_by_type(SearchBar)
72 return search_bar
73
74
75class SearchBar(UnityIntrospectionObject):
76 """The search bar for the spread filter."""
54\ No newline at end of file77\ No newline at end of file
5578
=== modified file 'tests/autopilot/unity/tests/test_spread.py'
--- tests/autopilot/unity/tests/test_spread.py 2014-02-14 16:40:28 +0000
+++ tests/autopilot/unity/tests/test_spread.py 2014-02-18 00:59:55 +0000
@@ -12,7 +12,6 @@
12from autopilot.matchers import Eventually12from autopilot.matchers import Eventually
13from testtools.matchers import Equals, NotEquals13from testtools.matchers import Equals, NotEquals
14from time import sleep14from time import sleep
15from unity.emulators.icons import BFBLauncherIcon
1615
17from unity.tests import UnityTestCase16from unity.tests import UnityTestCase
1817
@@ -56,10 +55,14 @@
56 self.launcher.click_launcher_icon(icon, move_mouse_after=False)55 self.launcher.click_launcher_icon(icon, move_mouse_after=False)
57 self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True)))56 self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True)))
5857
59 def assertWindowIsNotScaled(self, window):58 def get_spread_filter(self):
60 """Assert that a window is not scaled"""59 self.assertThat(lambda: self.unity.screen.spread_filter, Eventually(NotEquals(None)))
61 refresh_fn = lambda: window.id in [w.id for w in self.unity.screen.scaled_windows]60 return self.unity.screen.spread_filter
62 self.assertThat(refresh_fn, Eventually(Equals(False)))61
62 def assertWindowIsScaledEquals(self, xid, scaled):
63 """Assert weather a window is scaled"""
64 refresh_fn = lambda: xid in [w.xid for w in self.unity.screen.scaled_windows]
65 self.assertThat(refresh_fn, Eventually(Equals(scaled)))
6366
64 def assertWindowIsClosed(self, xid):67 def assertWindowIsClosed(self, xid):
65 """Assert that a window is not in the list of the open windows"""68 """Assert that a window is not in the list of the open windows"""
@@ -72,7 +75,7 @@
7275
73 def assertLauncherIconsDesaturated(self, also_active=True):76 def assertLauncherIconsDesaturated(self, also_active=True):
74 for icon in self.unity.launcher.model.get_launcher_icons():77 for icon in self.unity.launcher.model.get_launcher_icons():
75 if isinstance(icon, BFBLauncherIcon) or (not also_active and icon.active):78 if not also_active and icon.active:
76 self.assertFalse(icon.monitors_desaturated[self.monitor])79 self.assertFalse(icon.monitors_desaturated[self.monitor])
77 else:80 else:
78 self.assertTrue(icon.monitors_desaturated[self.monitor])81 self.assertTrue(icon.monitors_desaturated[self.monitor])
@@ -119,7 +122,7 @@
119 sleep(.5)122 sleep(.5)
120 self.mouse.click(button=2)123 self.mouse.click(button=2)
121124
122 self.assertWindowIsNotScaled(target_win)125 self.assertWindowIsScaledEquals(target_xid, False)
123 self.assertWindowIsClosed(target_xid)126 self.assertWindowIsClosed(target_xid)
124127
125 def test_scaled_window_closes_on_close_button_click(self):128 def test_scaled_window_closes_on_close_button_click(self):
@@ -135,7 +138,7 @@
135 sleep(.5)138 sleep(.5)
136 self.mouse.click()139 self.mouse.click()
137140
138 self.assertWindowIsNotScaled(target_win)141 self.assertWindowIsScaledEquals(target_xid, False)
139 self.assertWindowIsClosed(target_xid)142 self.assertWindowIsClosed(target_xid)
140143
141 def test_spread_desaturate_launcher_icons(self):144 def test_spread_desaturate_launcher_icons(self):
@@ -191,3 +194,41 @@
191194
192 self.initiate_spread_for_screen()195 self.initiate_spread_for_screen()
193 self.assertThat(icon.get_tooltip().active, Eventually(Equals(False)))196 self.assertThat(icon.get_tooltip().active, Eventually(Equals(False)))
197
198 def test_spread_puts_panel_in_overlay_mode(self):
199 """Test that the panel is in overlay mode when in spread"""
200 self.start_test_application_windows("Calculator", 1)
201 self.initiate_spread_for_screen()
202 self.assertThat(self.unity.panels.get_active_panel().in_overlay_mode, Eventually(Equals(True)))
203 self.unity.window_manager.terminate_spread()
204 self.assertThat(self.unity.panels.get_active_panel().in_overlay_mode, Eventually(Equals(False)))
205
206 def test_panel_close_window_button_terminates_spread(self):
207 """Test that the panel close window button terminates the spread"""
208 self.start_test_application_windows("Calculator", 1)
209 self.initiate_spread_for_screen()
210 self.unity.panels.get_active_panel().window_buttons.close.mouse_click();
211 self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(False)))
212
213 def test_spread_filter(self):
214 """Test spread filter"""
215 cal_wins = self.start_test_application_windows("Calculator", 2)
216 char_wins = self.start_test_application_windows("Character Map", 2)
217 self.initiate_spread_for_screen()
218 spread_filter = self.get_spread_filter()
219 self.assertThat(spread_filter.visible, Eventually(Equals(False)))
220
221 self.addCleanup(self.keyboard.press_and_release, "Escape")
222 self.keyboard.type(cal_wins[0].title)
223 self.assertThat(spread_filter.visible, Eventually(Equals(True)))
224 self.assertThat(spread_filter.search_bar.search_string, Eventually(Equals(cal_wins[0].title)))
225
226 for w in cal_wins + char_wins:
227 self.assertWindowIsScaledEquals(w.x_id, (w in cal_wins))
228
229 self.keyboard.press_and_release("Escape")
230 self.assertThat(spread_filter.visible, Eventually(Equals(False)))
231 self.assertThat(spread_filter.search_bar.search_string, Eventually(Equals("")))
232
233 for w in cal_wins + char_wins:
234 self.assertWindowIsScaledEquals(w.x_id, True)
194235
=== added file 'tests/test_spread_filter.cpp'
--- tests/test_spread_filter.cpp 1970-01-01 00:00:00 +0000
+++ tests/test_spread_filter.cpp 2014-02-18 00:59:55 +0000
@@ -0,0 +1,116 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * version 3 along with this program. If not, see
15 * <http://www.gnu.org/licenses/>
16 *
17 * Authored by: Marco Trevisan <marco.trevisan@canonical.com>
18 *
19 */
20
21#include <gmock/gmock.h>
22#include <Nux/NuxTimerTickSource.h>
23#include <NuxCore/AnimationController.h>
24
25#include "SpreadFilter.h"
26#include "UnitySettings.h"
27#include "DashStyle.h"
28#include "test_utils.h"
29
30namespace unity
31{
32namespace spread
33{
34namespace
35{
36using namespace testing;
37
38const unsigned ANIMATION_DURATION = 100 * 1000; // in microseconds
39
40struct SigReceiver : sigc::trackable
41{
42 typedef NiceMock<SigReceiver> Nice;
43
44 SigReceiver(Filter const& const_filter)
45 {
46 auto& filter = const_cast<Filter&>(const_filter);
47 filter.text.changed.connect(sigc::mem_fun(this, &SigReceiver::TextChanged));
48 }
49
50 MOCK_CONST_METHOD1(TextChanged, void(std::string const&));
51};
52
53struct TestSpreadFilter : Test
54{
55 TestSpreadFilter()
56 : animation_controller(tick_source)
57 , big_tick_(0)
58 , sig_receiver(filter)
59 {}
60
61 void Tick()
62 {
63 big_tick_ += ANIMATION_DURATION;
64 tick_source.tick(big_tick_);
65 }
66
67 Settings settings_;
68 dash::Style style_;
69 nux::NuxTimerTickSource tick_source;
70 nux::animation::AnimationController animation_controller;
71 uint64_t big_tick_;
72 Filter filter;
73 SigReceiver::Nice sig_receiver;
74};
75
76TEST_F(TestSpreadFilter, Construction)
77{
78 EXPECT_FALSE(filter.Visible());
79 EXPECT_TRUE(filter.text().empty());
80}
81
82TEST_F(TestSpreadFilter, VisibleWithText)
83{
84 std::string filter_string = "Unity is cool!";
85 EXPECT_CALL(sig_receiver, TextChanged(_)).Times(0);
86
87 filter.text = filter_string;
88 Tick();
89
90 EXPECT_TRUE(filter.Visible());
91 EXPECT_FALSE(filter.text().empty());
92
93 EXPECT_CALL(sig_receiver, TextChanged(filter_string));
94 Utils::WaitForTimeoutMSec();
95
96 EXPECT_EQ(filter_string, filter.text());
97}
98
99TEST_F(TestSpreadFilter, InVisibleWithoutText)
100{
101 filter.text = "Really, Unity is cool!";
102 Utils::WaitForTimeoutMSec();
103 Tick();
104
105 ASSERT_TRUE(filter.Visible());
106
107 EXPECT_CALL(sig_receiver, TextChanged(""));
108 filter.text = "";
109 EXPECT_TRUE(filter.text().empty());
110 Tick();
111 EXPECT_FALSE(filter.Visible());
112}
113
114} // anonymous namespace
115} // spread namespace
116} // unity namespace
0117
=== modified file 'unity-shared/CMakeLists.txt'
--- unity-shared/CMakeLists.txt 2014-02-12 07:09:43 +0000
+++ unity-shared/CMakeLists.txt 2014-02-18 00:59:55 +0000
@@ -55,6 +55,7 @@
55 ResizingBaseWindow.cpp55 ResizingBaseWindow.cpp
56 SearchBar.cpp56 SearchBar.cpp
57 SearchBarSpinner.cpp57 SearchBarSpinner.cpp
58 SpreadFilter.cpp
58 StaticCairoText.cpp59 StaticCairoText.cpp
59 TextureCache.cpp60 TextureCache.cpp
60 TextInput.cpp61 TextInput.cpp
6162
=== modified file 'unity-shared/PluginAdapter.cpp'
--- unity-shared/PluginAdapter.cpp 2014-02-18 00:59:55 +0000
+++ unity-shared/PluginAdapter.cpp 2014-02-18 00:59:55 +0000
@@ -24,6 +24,7 @@
24#include "PluginAdapter.h"24#include "PluginAdapter.h"
25#include "CompizUtils.h"25#include "CompizUtils.h"
2626
27#include <scale/scale.h>
27#include <NuxCore/Logger.h>28#include <NuxCore/Logger.h>
2829
29namespace unity30namespace unity
@@ -288,7 +289,7 @@
288289
289 if (primary_action_)290 if (primary_action_)
290 {291 {
291 primary_action_->terminate()(primary_action_, 0, argument);292 primary_action_->terminate()(primary_action_, CompAction::StateCancel, argument);
292 return;293 return;
293 }294 }
294295
@@ -337,13 +338,8 @@
337{338{
338 std::ostringstream sout;339 std::ostringstream sout;
339340
340 sout << "any & (";
341
342 for (auto const& window : windows)341 for (auto const& window : windows)
343 {342 sout << "xid=" << window << " | ";
344 sout << "| xid=" << window << " ";
345 }
346 sout << ")";
347343
348 return sout.str();344 return sout.str();
349}345}
350346
=== modified file 'unity-shared/SearchBar.cpp'
--- unity-shared/SearchBar.cpp 2013-11-19 18:48:35 +0000
+++ unity-shared/SearchBar.cpp 2014-02-18 00:59:55 +0000
@@ -33,8 +33,8 @@
3333
34namespace34namespace
35{35{
36const float kExpandDefaultIconOpacity = 1.0f;36const float DEFAULT_ICON_OPACITY = 1.0f;
37const int LIVE_SEARCH_TIMEOUT = 40;37const int DEFAULT_LIVE_SEARCH_TIMEOUT = 40;
38const int SPINNER_TIMEOUT = 100;38const int SPINNER_TIMEOUT = 100;
3939
40const int SPACE_BETWEEN_SPINNER_AND_TEXT = 5;40const int SPACE_BETWEEN_SPINNER_AND_TEXT = 5;
@@ -105,34 +105,19 @@
105NUX_IMPLEMENT_OBJECT_TYPE(SearchBar);105NUX_IMPLEMENT_OBJECT_TYPE(SearchBar);
106106
107SearchBar::SearchBar(NUX_FILE_LINE_DECL)107SearchBar::SearchBar(NUX_FILE_LINE_DECL)
108 : View(NUX_FILE_LINE_PARAM)108 : SearchBar(false)
109 , search_hint("")109{}
110 , showing_filters(false)110
111 , can_refine_search(false)111SearchBar::SearchBar(bool show_filter_hint, NUX_FILE_LINE_DECL)
112 , show_filter_hint_(true)112 : View(NUX_FILE_LINE_PARAM)
113 , expander_view_(nullptr)113 , showing_filters(false)
114 , show_filters_(nullptr)114 , can_refine_search(false)
115 , last_width_(-1)115 , live_search_wait(DEFAULT_LIVE_SEARCH_TIMEOUT)
116 , last_height_(-1)116 , show_filter_hint_(show_filter_hint)
117{117 , expander_view_(nullptr)
118 Init();118 , show_filters_(nullptr)
119}119 , last_width_(-1)
120120 , last_height_(-1)
121SearchBar::SearchBar(bool show_filter_hint_, NUX_FILE_LINE_DECL)
122 : View(NUX_FILE_LINE_PARAM)
123 , search_hint("")
124 , showing_filters(false)
125 , can_refine_search(false)
126 , show_filter_hint_(show_filter_hint_)
127 , expander_view_(nullptr)
128 , show_filters_(nullptr)
129 , last_width_(-1)
130 , last_height_(-1)
131{
132 Init();
133}
134
135void SearchBar::Init()
136{121{
137 dash::Style& style = dash::Style::Instance();122 dash::Style& style = dash::Style::Instance();
138 nux::BaseTexture* icon = style.GetSearchMagnifyIcon();123 nux::BaseTexture* icon = style.GetSearchMagnifyIcon();
@@ -199,7 +184,7 @@
199 expand_icon_ = new IconTexture(arrow,184 expand_icon_ = new IconTexture(arrow,
200 arrow->GetWidth(),185 arrow->GetWidth(),
201 arrow->GetHeight());186 arrow->GetHeight());
202 expand_icon_->SetOpacity(kExpandDefaultIconOpacity);187 expand_icon_->SetOpacity(DEFAULT_ICON_OPACITY);
203 expand_icon_->SetMinimumSize(arrow->GetWidth(), arrow->GetHeight());188 expand_icon_->SetMinimumSize(arrow->GetWidth(), arrow->GetHeight());
204 expand_icon_->SetVisible(false);189 expand_icon_->SetVisible(false);
205190
@@ -294,10 +279,13 @@
294 font_desc << pango_font_description_get_family(desc) << " " << HINT_LABEL_FONT_STYLE << " " << HINT_LABEL_FONT_SIZE;279 font_desc << pango_font_description_get_family(desc) << " " << HINT_LABEL_FONT_STYLE << " " << HINT_LABEL_FONT_SIZE;
295 hint_->SetFont(font_desc.str().c_str());280 hint_->SetFont(font_desc.str().c_str());
296281
297 font_desc.str("");282 if (show_filter_hint_)
298 font_desc.clear();283 {
299 font_desc << pango_font_description_get_family(desc) << " " << SHOW_FILTERS_LABEL_FONT_STYLE << " " << SHOW_FILTERS_LABEL_FONT_SIZE;284 font_desc.str("");
300 show_filters_->SetFont(font_desc.str().c_str());285 font_desc.clear();
286 font_desc << pango_font_description_get_family(desc) << " " << SHOW_FILTERS_LABEL_FONT_STYLE << " " << SHOW_FILTERS_LABEL_FONT_SIZE;
287 show_filters_->SetFont(font_desc.str().c_str());
288 }
301289
302 pango_font_description_free(desc);290 pango_font_description_free(desc);
303 }291 }
@@ -317,7 +305,7 @@
317 // We don't want to set a new search string on every new character, so we add a sma305 // We don't want to set a new search string on every new character, so we add a sma
318 // timeout to see if the user is typing a sentence. If more characters are added, we306 // timeout to see if the user is typing a sentence. If more characters are added, we
319 // keep restarting the timeout unti the user has actuay paused.307 // keep restarting the timeout unti the user has actuay paused.
320 live_search_timeout_.reset(new glib::Timeout(LIVE_SEARCH_TIMEOUT));308 live_search_timeout_.reset(new glib::Timeout(live_search_wait()));
321 live_search_timeout_->Run(sigc::mem_fun(this, &SearchBar::OnLiveSearchTimeout));309 live_search_timeout_->Run(sigc::mem_fun(this, &SearchBar::OnLiveSearchTimeout));
322310
323 // Don't animate the spinner immediately, the searches are fast and311 // Don't animate the spinner immediately, the searches are fast and
@@ -428,6 +416,9 @@
428 nux::GetPainter().PushPaintLayerStack(); 416 nux::GetPainter().PushPaintLayerStack();
429 }417 }
430418
419 if (!IsFullRedraw())
420 graphics::ClearGeometry(pango_entry_->GetGeometry());
421
431 layout_->ProcessDraw(graphics_engine, force_draw);422 layout_->ProcessDraw(graphics_engine, force_draw);
432423
433 if (IsFullRedraw())424 if (IsFullRedraw())
@@ -455,7 +446,7 @@
455446
456void SearchBar::ForceLiveSearch()447void SearchBar::ForceLiveSearch()
457{448{
458 live_search_timeout_.reset(new glib::Timeout(LIVE_SEARCH_TIMEOUT));449 live_search_timeout_.reset(new glib::Timeout(live_search_wait()));
459 live_search_timeout_->Run(sigc::mem_fun(this, &SearchBar::OnLiveSearchTimeout));450 live_search_timeout_->Run(sigc::mem_fun(this, &SearchBar::OnLiveSearchTimeout));
460451
461 start_spinner_timeout_.reset(new glib::Timeout(SPINNER_TIMEOUT));452 start_spinner_timeout_.reset(new glib::Timeout(SPINNER_TIMEOUT));
@@ -466,8 +457,7 @@
466{457{
467 start_spinner_timeout_.reset();458 start_spinner_timeout_.reset();
468459
469 bool is_empty = pango_entry_->im_active() ?460 bool is_empty = pango_entry_->im_active() ? false : pango_entry_->GetText().empty();
470 false : pango_entry_->GetText() == "";
471 spinner_->SetState(is_empty ? STATE_READY : STATE_CLEAR);461 spinner_->SetState(is_empty ? STATE_READY : STATE_CLEAR);
472}462}
473463
@@ -609,14 +599,19 @@
609 .add(GetAbsoluteGeometry())599 .add(GetAbsoluteGeometry())
610 .add("has_focus", pango_entry_->HasKeyFocus())600 .add("has_focus", pango_entry_->HasKeyFocus())
611 .add("search_string", pango_entry_->GetText())601 .add("search_string", pango_entry_->GetText())
612 .add("expander-has-focus", expander_view_->HasKeyFocus())
613 .add("showing-filters", showing_filters)602 .add("showing-filters", showing_filters)
614 .add("filter-label-x", show_filters_->GetAbsoluteX())
615 .add("filter-label-y", show_filters_->GetAbsoluteY())
616 .add("filter-label-width", show_filters_->GetAbsoluteWidth())
617 .add("filter-label-height", show_filters_->GetAbsoluteHeight())
618 .add("filter-label-geo", show_filters_->GetAbsoluteGeometry())
619 .add("im_active", pango_entry_->im_active());603 .add("im_active", pango_entry_->im_active());
604
605 if (show_filter_hint_)
606 {
607 introspection
608 .add("expander-has-focus", expander_view_->HasKeyFocus())
609 .add("filter-label-x", show_filters_->GetAbsoluteX())
610 .add("filter-label-y", show_filters_->GetAbsoluteY())
611 .add("filter-label-width", show_filters_->GetAbsoluteWidth())
612 .add("filter-label-height", show_filters_->GetAbsoluteHeight())
613 .add("filter-label-geo", show_filters_->GetAbsoluteGeometry());
614 }
620}615}
621616
622} // namespace unity617} // namespace unity
623618
=== modified file 'unity-shared/SearchBar.h'
--- unity-shared/SearchBar.h 2013-09-19 16:44:03 +0000
+++ unity-shared/SearchBar.h 2014-02-18 00:59:55 +0000
@@ -63,14 +63,13 @@
63 nux::Property<bool> can_refine_search;63 nux::Property<bool> can_refine_search;
64 nux::ROProperty<bool> im_active;64 nux::ROProperty<bool> im_active;
65 nux::ROProperty<bool> im_preedit;65 nux::ROProperty<bool> im_preedit;
66 nux::Property<unsigned> live_search_wait;
6667
67 sigc::signal<void> activated;68 sigc::signal<void> activated;
68 sigc::signal<void, std::string const&> search_changed;69 sigc::signal<void, std::string const&> search_changed;
69 sigc::signal<void, std::string const&> live_search_reached;70 sigc::signal<void, std::string const&> live_search_reached;
7071
71private:72private:
72 void Init();
73
74 void OnFontChanged(GtkSettings* settings, GParamSpec* pspec=NULL);73 void OnFontChanged(GtkSettings* settings, GParamSpec* pspec=NULL);
75 void OnSearchHintChanged();74 void OnSearchHintChanged();
7675
7776
=== added file 'unity-shared/SpreadFilter.cpp'
--- unity-shared/SpreadFilter.cpp 1970-01-01 00:00:00 +0000
+++ unity-shared/SpreadFilter.cpp 2014-02-18 00:59:55 +0000
@@ -0,0 +1,121 @@
1// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2/*
3* Copyright (C) 2014 Canonical Ltd
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License version 3 as
7* published by the Free Software Foundation.
8*
9* This program is distributed in the hope that it will be useful,
10* but WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12* GNU General Public License for more details.
13*
14* You should have received a copy of the GNU General Public License
15* along with this program. If not, see <http://www.gnu.org/licenses/>.
16*
17* Authored by: Marco Trevisan <marco@ubuntu.com>
18*/
19
20#include "SpreadFilter.h"
21
22#include <Nux/HLayout.h>
23#include "AnimationUtils.h"
24#include "SearchBar.h"
25#include "WindowManager.h"
26
27namespace unity
28{
29namespace spread
30{
31namespace
32{
33const unsigned FADE_DURATION = 100;
34const unsigned DEFAULT_SEARCH_WAIT = 300;
35const nux::Point OFFSET(10, 15);
36const nux::Size SIZE(620, 42);
37}
38
39Filter::Filter()
40 : fade_animator_(FADE_DURATION)
41{
42 search_bar_ = SearchBar::Ptr(new SearchBar());
43 search_bar_->SetMinMaxSize(SIZE.width, SIZE.height);
44 search_bar_->live_search_wait = DEFAULT_SEARCH_WAIT;
45 text.SetGetterFunction([this] { return search_bar_->search_string(); });
46 text.SetSetterFunction([this] (std::string const& t) { search_bar_->search_string = t; return false; });
47 debug::Introspectable::AddChild(search_bar_.GetPointer());
48
49 auto layout = new nux::HLayout(NUX_TRACKER_LOCATION);
50 layout->SetVerticalExternalMargin(0);
51 layout->SetHorizontalExternalMargin(0);
52 layout->AddView(search_bar_.GetPointer());
53
54 auto const& work_area = WindowManager::Default().GetWorkAreaGeometry(0);
55 view_window_ = new nux::BaseWindow(GetName().c_str());
56 view_window_->SetLayout(layout);
57 view_window_->SetBackgroundColor(nux::color::Transparent);
58 view_window_->SetWindowSizeMatchLayout(true);
59 view_window_->ShowWindow(true);
60 view_window_->PushToFront();
61 view_window_->SetOpacity(0.0f);
62 view_window_->SetEnterFocusInputArea(search_bar_.GetPointer());
63 view_window_->SetInputFocus();
64 view_window_->SetXY(OFFSET.x + work_area.x, OFFSET.y + work_area.y);
65 fade_animator_.updated.connect([this] (double opacity) { view_window_->SetOpacity(opacity); });
66
67 nux::GetWindowCompositor().SetKeyFocusArea(search_bar_->text_entry());
68
69 search_bar_->search_changed.connect([this] (std::string const& search) {
70 if (!Visible())
71 animation::StartOrReverse(fade_animator_, animation::Direction::FORWARD);
72
73 if (search.empty())
74 {
75 text.changed.emit(search);
76 animation::StartOrReverse(fade_animator_, animation::Direction::BACKWARD);
77 }
78 });
79
80 search_bar_->live_search_reached.connect([this] (std::string const& search) {
81 if (!search.empty())
82 {
83 text.changed.emit(search);
84 search_bar_->SetSearchFinished();
85 }
86 });
87}
88
89Filter::~Filter()
90{
91 nux::GetWindowCompositor().SetKeyFocusArea(nullptr);
92 nux::GetWindowThread()->RemoveObjectFromLayoutQueue(view_window_.GetPointer());
93}
94
95bool Filter::Visible() const
96{
97 return (view_window_->GetOpacity() != 0.0f);
98}
99
100nux::Geometry const& Filter::GetAbsoluteGeometry() const
101{
102 return view_window_->GetGeometry();
103}
104
105//
106// Introspection
107//
108std::string Filter::GetName() const
109{
110 return "SpreadFilter";
111}
112
113void Filter::AddProperties(debug::IntrospectionData& introspection)
114{
115 introspection
116 .add(GetAbsoluteGeometry())
117 .add("visible", Visible());
118}
119
120} // namespace spread
121} // namespace unity
0122
=== added file 'unity-shared/SpreadFilter.h'
--- unity-shared/SpreadFilter.h 1970-01-01 00:00:00 +0000
+++ unity-shared/SpreadFilter.h 2014-02-18 00:59:55 +0000
@@ -0,0 +1,64 @@
1// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2/*
3* Copyright (C) 2014 Canonical Ltd
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License version 3 as
7* published by the Free Software Foundation.
8*
9* This program is distributed in the hope that it will be useful,
10* but WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12* GNU General Public License for more details.
13*
14* You should have received a copy of the GNU General Public License
15* along with this program. If not, see <http://www.gnu.org/licenses/>.
16*
17* Authored by: Marco Trevisan <marco@ubuntu.com>
18*/
19
20#ifndef UNITYSHELL_SPREAD_FILTER_H
21#define UNITYSHELL_SPREAD_FILTER_H
22
23#include <memory>
24
25#include <Nux/Nux.h>
26#include <Nux/BaseWindow.h>
27#include <NuxCore/Animation.h>
28#include "Introspectable.h"
29
30namespace unity
31{
32class SearchBar;
33
34namespace spread
35{
36
37class Filter : public debug::Introspectable, public sigc::trackable
38{
39public:
40 typedef std::shared_ptr<Filter> Ptr;
41
42 Filter();
43 virtual ~Filter();
44
45 nux::RWProperty<std::string> text;
46
47 bool Visible() const;
48 nux::Geometry const& GetAbsoluteGeometry() const;
49
50protected:
51 // Introspectable
52 std::string GetName() const;
53 void AddProperties(debug::IntrospectionData&);
54
55private:
56 nux::ObjectPtr<SearchBar> search_bar_;
57 nux::ObjectPtr<nux::BaseWindow> view_window_;
58 nux::animation::AnimateValue<double> fade_animator_;
59};
60
61} // namespace spread
62} // namespace unity
63
64#endif