diff options
| author | Marco Trevisan (TreviƱo) <mail@3v1n0.net> | 2014-02-18 16:52:35 +0000 |
|---|---|---|
| committer | CI bot <ps-jenkins@lists.canonical.com> | 2014-02-18 16:52:35 +0000 |
| commit | 5552af01f7a0d146fdf92a7c1c9181c90ac116f0 (patch) | |
| tree | 2a830786d7145179f22af886a20167d37d48b0e1 | |
| parent | 4a7c9922101362e50ef439d63ee4735264bd1251 (diff) | |
| parent | 7784579293f26ca8e83f9c934ad814cce8d8fede (diff) | |
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! Fixes: 1281257 (bzr r3668)
| -rw-r--r-- | plugins/unityshell/src/unityshell.cpp | 90 | ||||
| -rw-r--r-- | plugins/unityshell/src/unityshell.h | 4 | ||||
| -rw-r--r-- | plugins/unityshell/unityshell.xml.in | 1 | ||||
| -rw-r--r-- | shutdown/SessionController.h | 1 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/autopilot/unity/emulators/screen.py | 23 | ||||
| -rw-r--r-- | tests/autopilot/unity/tests/test_spread.py | 57 | ||||
| -rw-r--r-- | tests/test_spread_filter.cpp | 116 | ||||
| -rw-r--r-- | unity-shared/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | unity-shared/PluginAdapter.cpp | 10 | ||||
| -rw-r--r-- | unity-shared/SearchBar.cpp | 69 | ||||
| -rw-r--r-- | unity-shared/SearchBar.h | 3 | ||||
| -rw-r--r-- | unity-shared/SpreadFilter.cpp | 121 | ||||
| -rw-r--r-- | unity-shared/SpreadFilter.h | 64 |
14 files changed, 476 insertions, 85 deletions
diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index cdaa6dcd9..340844006 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -137,10 +137,8 @@ const unsigned int SCROLL_DOWN_BUTTON = 6; const unsigned int SCROLL_UP_BUTTON = 7; const int MAX_BUFFER_AGE = 11; const int FRAMES_TO_REDRAW_ON_RESUME = 10; - const std::string RELAYOUT_TIMEOUT = "relayout-timeout"; } // namespace local - } // anon namespace UnityScreen::UnityScreen(CompScreen* screen) @@ -149,6 +147,7 @@ UnityScreen::UnityScreen(CompScreen* screen) , screen(screen) , cScreen(CompositeScreen::get(screen)) , gScreen(GLScreen::get(screen)) + , sScreen(ScaleScreen::get(screen)) , menus_(std::make_shared<menu::Manager>(std::make_shared<indicator::DBusIndicators>(), std::make_shared<key::GnomeGrabber>())) , deco_manager_(std::make_shared<decoration::Manager>()) , debugger_(this) @@ -511,13 +510,28 @@ void UnityScreen::initAltTabNextWindow() void UnityScreen::OnInitiateSpread() { - for (auto const& swin : ScaleScreen::get(screen)->getWindows()) + spread_filter_ = std::make_shared<spread::Filter>(); + spread_filter_->text.changed.connect([this] (std::string const& filter) { + if (filter.empty()) + { + sScreen->relayoutSlots(CompMatch::emptyMatch); + } + else + { + auto match = sScreen->getCustomMatch(); + sScreen->relayoutSlots(match & ("ititle="+filter)); + } + }); + + for (auto const& swin : sScreen->getWindows()) UnityWindow::get(swin->window)->OnInitiateSpread(); } void UnityScreen::OnTerminateSpread() { - for (auto const& swin : ScaleScreen::get(screen)->getWindows()) + spread_filter_.reset(); + + for (auto const& swin : sScreen->getWindows()) UnityWindow::get(swin->window)->OnTerminateSpread(); } @@ -1680,8 +1694,7 @@ void UnityScreen::handleEvent(XEvent* event) case MotionNotify: if (wm.IsScaleActive()) { - ScaleScreen* ss = ScaleScreen::get(screen); - if (CompWindow *w = screen->findWindow(ss->getSelectedWindow())) + if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } else if (switcher_controller_->IsDetailViewShown()) @@ -1706,9 +1719,14 @@ void UnityScreen::handleEvent(XEvent* event) } if (wm.IsScaleActive()) { - ScaleScreen* ss = ScaleScreen::get(screen); - if (CompWindow *w = screen->findWindow(ss->getSelectedWindow())) - skip_other_plugins = UnityWindow::get(w)->handleEvent(event); + if (spread_filter_ && spread_filter_->Visible()) + skip_other_plugins = spread_filter_->GetAbsoluteGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root); + + if (!skip_other_plugins) + { + if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) + skip_other_plugins = UnityWindow::get(w)->handleEvent(event); + } } else if (switcher_controller_->IsDetailViewShown()) { @@ -1782,9 +1800,14 @@ void UnityScreen::handleEvent(XEvent* event) } else if (wm.IsScaleActive()) { - ScaleScreen* ss = ScaleScreen::get(screen); - if (CompWindow *w = screen->findWindow(ss->getSelectedWindow())) - skip_other_plugins = UnityWindow::get(w)->handleEvent(event); + if (spread_filter_ && spread_filter_->Visible()) + skip_other_plugins = spread_filter_->GetAbsoluteGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root); + + if (!skip_other_plugins) + { + if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) + skip_other_plugins = skip_other_plugins || UnityWindow::get(w)->handleEvent(event); + } } break; case KeyPress: @@ -1853,6 +1876,16 @@ void UnityScreen::handleEvent(XEvent* event) EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); } } + + if (spread_filter_ && spread_filter_->Visible()) + { + if (key_sym == XK_Escape) + { + skip_other_plugins = true; + spread_filter_->text = ""; + } + } + break; } case MapRequest: @@ -1866,22 +1899,21 @@ void UnityScreen::handleEvent(XEvent* event) } break; default: - if (screen->shapeEvent () + ShapeNotify == event->type) + if (screen->shapeEvent() + ShapeNotify == event->type) { Window xid = event->xany.window; CompWindow *w = screen->findWindow(xid); if (w) { - UnityWindow *uw = UnityWindow::get (w); - + UnityWindow *uw = UnityWindow::get(w); uw->handleEvent(event); } } break; } - compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow>::handleEvent (event); + compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow>::handleEvent(event); // avoid further propagation (key conflict for instance) if (!skip_other_plugins) @@ -1890,19 +1922,18 @@ void UnityScreen::handleEvent(XEvent* event) if (deco_manager_->HandleEventAfter(event)) return; - switch (event->type) - { - case MapRequest: - ShowdesktopHandler::AllowLeaveShowdesktopMode(event->xmaprequest.window); - break; - } + if (event->type == MapRequest) + ShowdesktopHandler::AllowLeaveShowdesktopMode(event->xmaprequest.window); - if ((event->type == MotionNotify || event->type == ButtonPress || event->type == ButtonRelease) && - switcher_controller_->IsMouseDisabled() && switcher_controller_->Visible()) + if (switcher_controller_->IsMouseDisabled() && switcher_controller_->Visible() && + (event->type == MotionNotify || event->type == ButtonPress || event->type == ButtonRelease)) { skip_other_plugins = true; } + if (spread_filter_ && spread_filter_->Visible()) + skip_other_plugins = false; + if (!skip_other_plugins && screen->otherGrabExist("deco", "move", "switcher", "resize", nullptr)) { @@ -2786,7 +2817,7 @@ bool UnityWindow::glPaint(const GLWindowPaintAttrib& attrib, } if (WindowManager::Default().IsScaleActive() && - ScaleScreen::get(screen)->getSelectedWindow() == window->id()) + uScreen->sScreen->getSelectedWindow() == window->id()) { nux::Geometry const& scaled_geo = GetScaledGeometry(); paintInnerGlow(scaled_geo, matrix, attrib, mask); @@ -3737,7 +3768,7 @@ UnityWindow::UnityWindow(CompWindow* window) void UnityWindow::AddProperties(debug::IntrospectionData& introspection) { Window xid = window->id(); - auto const& swins = ScaleScreen::get(screen)->getWindows(); + auto const& swins = uScreen->sScreen->getWindows(); bool scaled = std::find(swins.begin(), swins.end(), ScaleWindow::get(window)) != swins.end(); WindowManager& wm = WindowManager::Default(); @@ -3942,8 +3973,7 @@ void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib, if (!scale_win->hasSlot()) // animation not finished return; - ScaleScreen* ss = ScaleScreen::get(screen); - auto state = ss->getState(); + auto state = uScreen->sScreen->getState(); if (state != ScaleScreen::Wait && state != ScaleScreen::Out) return; @@ -3953,7 +3983,7 @@ void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib, auto deco_attrib = attrib; deco_attrib.opacity = COMPIZ_COMPOSITE_OPAQUE; - bool highlighted = (ss->getSelectedWindow() == window->id()); + bool highlighted = (uScreen->sScreen->getSelectedWindow() == window->id()); paintFakeDecoration(scale_geo, deco_attrib, transform, mask, highlighted, pos.scale); } @@ -4130,7 +4160,7 @@ void ScreenIntrospection::AddProperties(debug::IntrospectionData& introspection) Introspectable::IntrospectableList ScreenIntrospection::GetIntrospectableChildren() { - IntrospectableList children; + IntrospectableList children({uScreen->spread_filter_.get()}); for (auto const& win : screen_->windows()) children.push_back(UnityWindow::get(win)); diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index bbb94ece4..a9594f7c2 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -65,6 +65,7 @@ #include "ScreenIntrospection.h" #include "SwitcherController.h" #include "SessionController.h" +#include "SpreadFilter.h" #include "UBusWrapper.h" #include "UnityshellPrivate.h" #include "UnityShowdesktopHandler.h" @@ -118,6 +119,7 @@ public: CompScreen* screen; CompositeScreen* cScreen; GLScreen* gScreen; + ScaleScreen* sScreen; /* prepares nux for drawing */ void nuxPrologue(); @@ -327,6 +329,7 @@ private: session::Controller::Ptr session_controller_; debug::DebugDBusInterface debugger_; std::unique_ptr<BGHash> bghash_; + spread::Filter::Ptr spread_filter_; /* Subscription for gestures that manipulate Unity launcher */ std::unique_ptr<nux::GesturesSubscription> gestures_sub_launcher_; @@ -407,6 +410,7 @@ private: bool is_desktop_active_; friend class UnityWindow; + friend class debug::ScreenIntrospection; friend class decoration::Manager; }; diff --git a/plugins/unityshell/unityshell.xml.in b/plugins/unityshell/unityshell.xml.in index ac484f319..87e8d618e 100644 --- a/plugins/unityshell/unityshell.xml.in +++ b/plugins/unityshell/unityshell.xml.in @@ -43,6 +43,7 @@ </requirement> <conflict> <plugin>decor</plugin> + <plugin>scalefilter</plugin> </conflict> </deps> diff --git a/shutdown/SessionController.h b/shutdown/SessionController.h index 3c5ab97ce..59fe6a9c7 100644 --- a/shutdown/SessionController.h +++ b/shutdown/SessionController.h @@ -25,7 +25,6 @@ #include <Nux/Nux.h> #include <Nux/BaseWindow.h> #include <Nux/HLayout.h> -#include <NuxCore/Color.h> #include <NuxCore/Animation.h> #include <UnityCore/SessionManager.h> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 770ed22b0..86f88a772 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -278,6 +278,7 @@ if (ENABLE_X_SUPPORT) test_single_monitor_launcher_icon.cpp test_showdesktop_handler.cpp test_software_center_launcher_icon.cpp + test_spread_filter.cpp test_static_cairo_text.cpp test_switcher_controller.cpp test_switcher_controller_class.cpp diff --git a/tests/autopilot/unity/emulators/screen.py b/tests/autopilot/unity/emulators/screen.py index 94179ef67..29f2e37e5 100644 --- a/tests/autopilot/unity/emulators/screen.py +++ b/tests/autopilot/unity/emulators/screen.py @@ -28,6 +28,15 @@ class Screen(UnityIntrospectionObject): """Return the available scaled windows, or None.""" return self.get_children_by_type(Window, scaled=True) + @property + def spread_filter(self): + """Return the spread filter, or None.""" + filter = self.get_children_by_type(SpreadFilter) + if len(filter): + return filter[0] + + return None + def window(self, xid): """Return the window with given xid.""" windows = self.get_children_by_type(Window, xid=xid) @@ -51,3 +60,17 @@ class Window(UnityIntrospectionObject): self.scaled_close_width.wait_for(GreaterThan(0)) self.scaled_close_height.wait_for(GreaterThan(0)) return (self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height) + + +class SpreadFilter(UnityIntrospectionObject): + """The spread filter.""" + + @property + def search_bar(self): + """Return the search bar.""" + [search_bar] = self.get_children_by_type(SearchBar) + return search_bar + + +class SearchBar(UnityIntrospectionObject): + """The search bar for the spread filter.""" \ No newline at end of file diff --git a/tests/autopilot/unity/tests/test_spread.py b/tests/autopilot/unity/tests/test_spread.py index 7c7887f47..c6e5b73ec 100644 --- a/tests/autopilot/unity/tests/test_spread.py +++ b/tests/autopilot/unity/tests/test_spread.py @@ -12,7 +12,6 @@ from autopilot.display import Display from autopilot.matchers import Eventually from testtools.matchers import Equals, NotEquals from time import sleep -from unity.emulators.icons import BFBLauncherIcon from unity.tests import UnityTestCase @@ -56,10 +55,14 @@ class SpreadTests(UnityTestCase): self.launcher.click_launcher_icon(icon, move_mouse_after=False) self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True))) - def assertWindowIsNotScaled(self, window): - """Assert that a window is not scaled""" - refresh_fn = lambda: window.id in [w.id for w in self.unity.screen.scaled_windows] - self.assertThat(refresh_fn, Eventually(Equals(False))) + def get_spread_filter(self): + self.assertThat(lambda: self.unity.screen.spread_filter, Eventually(NotEquals(None))) + return self.unity.screen.spread_filter + + def assertWindowIsScaledEquals(self, xid, scaled): + """Assert weather a window is scaled""" + refresh_fn = lambda: xid in [w.xid for w in self.unity.screen.scaled_windows] + self.assertThat(refresh_fn, Eventually(Equals(scaled))) def assertWindowIsClosed(self, xid): """Assert that a window is not in the list of the open windows""" @@ -72,7 +75,7 @@ class SpreadTests(UnityTestCase): def assertLauncherIconsDesaturated(self, also_active=True): for icon in self.unity.launcher.model.get_launcher_icons(): - if isinstance(icon, BFBLauncherIcon) or (not also_active and icon.active): + if not also_active and icon.active: self.assertFalse(icon.monitors_desaturated[self.monitor]) else: self.assertTrue(icon.monitors_desaturated[self.monitor]) @@ -119,7 +122,7 @@ class SpreadTests(UnityTestCase): sleep(.5) self.mouse.click(button=2) - self.assertWindowIsNotScaled(target_win) + self.assertWindowIsScaledEquals(target_xid, False) self.assertWindowIsClosed(target_xid) def test_scaled_window_closes_on_close_button_click(self): @@ -135,7 +138,7 @@ class SpreadTests(UnityTestCase): sleep(.5) self.mouse.click() - self.assertWindowIsNotScaled(target_win) + self.assertWindowIsScaledEquals(target_xid, False) self.assertWindowIsClosed(target_xid) def test_spread_desaturate_launcher_icons(self): @@ -191,3 +194,41 @@ class SpreadTests(UnityTestCase): self.initiate_spread_for_screen() self.assertThat(icon.get_tooltip().active, Eventually(Equals(False))) + + def test_spread_puts_panel_in_overlay_mode(self): + """Test that the panel is in overlay mode when in spread""" + self.start_test_application_windows("Calculator", 1) + self.initiate_spread_for_screen() + self.assertThat(self.unity.panels.get_active_panel().in_overlay_mode, Eventually(Equals(True))) + self.unity.window_manager.terminate_spread() + self.assertThat(self.unity.panels.get_active_panel().in_overlay_mode, Eventually(Equals(False))) + + def test_panel_close_window_button_terminates_spread(self): + """Test that the panel close window button terminates the spread""" + self.start_test_application_windows("Calculator", 1) + self.initiate_spread_for_screen() + self.unity.panels.get_active_panel().window_buttons.close.mouse_click(); + self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(False))) + + def test_spread_filter(self): + """Test spread filter""" + cal_wins = self.start_test_application_windows("Calculator", 2) + char_wins = self.start_test_application_windows("Character Map", 2) + self.initiate_spread_for_screen() + spread_filter = self.get_spread_filter() + self.assertThat(spread_filter.visible, Eventually(Equals(False))) + + self.addCleanup(self.keyboard.press_and_release, "Escape") + self.keyboard.type(cal_wins[0].title) + self.assertThat(spread_filter.visible, Eventually(Equals(True))) + self.assertThat(spread_filter.search_bar.search_string, Eventually(Equals(cal_wins[0].title))) + + for w in cal_wins + char_wins: + self.assertWindowIsScaledEquals(w.x_id, (w in cal_wins)) + + self.keyboard.press_and_release("Escape") + self.assertThat(spread_filter.visible, Eventually(Equals(False))) + self.assertThat(spread_filter.search_bar.search_string, Eventually(Equals(""))) + + for w in cal_wins + char_wins: + self.assertWindowIsScaledEquals(w.x_id, True) diff --git a/tests/test_spread_filter.cpp b/tests/test_spread_filter.cpp new file mode 100644 index 000000000..f4fd1e064 --- /dev/null +++ b/tests/test_spread_filter.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2014 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 warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY 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 + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Marco Trevisan <marco.trevisan@canonical.com> + * + */ + +#include <gmock/gmock.h> +#include <Nux/NuxTimerTickSource.h> +#include <NuxCore/AnimationController.h> + +#include "SpreadFilter.h" +#include "UnitySettings.h" +#include "DashStyle.h" +#include "test_utils.h" + +namespace unity +{ +namespace spread +{ +namespace +{ +using namespace testing; + +const unsigned ANIMATION_DURATION = 100 * 1000; // in microseconds + +struct SigReceiver : sigc::trackable +{ + typedef NiceMock<SigReceiver> Nice; + + SigReceiver(Filter const& const_filter) + { + auto& filter = const_cast<Filter&>(const_filter); + filter.text.changed.connect(sigc::mem_fun(this, &SigReceiver::TextChanged)); + } + + MOCK_CONST_METHOD1(TextChanged, void(std::string const&)); +}; + +struct TestSpreadFilter : Test +{ + TestSpreadFilter() + : animation_controller(tick_source) + , big_tick_(0) + , sig_receiver(filter) + {} + + void Tick() + { + big_tick_ += ANIMATION_DURATION; + tick_source.tick(big_tick_); + } + + Settings settings_; + dash::Style style_; + nux::NuxTimerTickSource tick_source; + nux::animation::AnimationController animation_controller; + uint64_t big_tick_; + Filter filter; + SigReceiver::Nice sig_receiver; +}; + +TEST_F(TestSpreadFilter, Construction) +{ + EXPECT_FALSE(filter.Visible()); + EXPECT_TRUE(filter.text().empty()); +} + +TEST_F(TestSpreadFilter, VisibleWithText) +{ + std::string filter_string = "Unity is cool!"; + EXPECT_CALL(sig_receiver, TextChanged(_)).Times(0); + + filter.text = filter_string; + Tick(); + + EXPECT_TRUE(filter.Visible()); + EXPECT_FALSE(filter.text().empty()); + + EXPECT_CALL(sig_receiver, TextChanged(filter_string)); + Utils::WaitForTimeoutMSec(); + + EXPECT_EQ(filter_string, filter.text()); +} + +TEST_F(TestSpreadFilter, InVisibleWithoutText) +{ + filter.text = "Really, Unity is cool!"; + Utils::WaitForTimeoutMSec(); + Tick(); + + ASSERT_TRUE(filter.Visible()); + + EXPECT_CALL(sig_receiver, TextChanged("")); + filter.text = ""; + EXPECT_TRUE(filter.text().empty()); + Tick(); + EXPECT_FALSE(filter.Visible()); +} + +} // anonymous namespace +} // spread namespace +} // unity namespace diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index c6b20fb66..5fca6ab17 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -55,6 +55,7 @@ set (UNITY_SHARED_SOURCES ResizingBaseWindow.cpp SearchBar.cpp SearchBarSpinner.cpp + SpreadFilter.cpp StaticCairoText.cpp TextureCache.cpp TextInput.cpp diff --git a/unity-shared/PluginAdapter.cpp b/unity-shared/PluginAdapter.cpp index 0a4611116..dc2c3b29a 100644 --- a/unity-shared/PluginAdapter.cpp +++ b/unity-shared/PluginAdapter.cpp @@ -24,6 +24,7 @@ #include "PluginAdapter.h" #include "CompizUtils.h" +#include <scale/scale.h> #include <NuxCore/Logger.h> namespace unity @@ -288,7 +289,7 @@ void MultiActionList::TerminateAll(CompOption::Vector const& extra_args) const if (primary_action_) { - primary_action_->terminate()(primary_action_, 0, argument); + primary_action_->terminate()(primary_action_, CompAction::StateCancel, argument); return; } @@ -337,13 +338,8 @@ std::string PluginAdapter::MatchStringForXids(std::vector<Window> const& windows { std::ostringstream sout; - sout << "any & ("; - for (auto const& window : windows) - { - sout << "| xid=" << window << " "; - } - sout << ")"; + sout << "xid=" << window << " | "; return sout.str(); } diff --git a/unity-shared/SearchBar.cpp b/unity-shared/SearchBar.cpp index f82cd39dd..12f055636 100644 --- a/unity-shared/SearchBar.cpp +++ b/unity-shared/SearchBar.cpp @@ -33,8 +33,8 @@ namespace { -const float kExpandDefaultIconOpacity = 1.0f; -const int LIVE_SEARCH_TIMEOUT = 40; +const float DEFAULT_ICON_OPACITY = 1.0f; +const int DEFAULT_LIVE_SEARCH_TIMEOUT = 40; const int SPINNER_TIMEOUT = 100; const int SPACE_BETWEEN_SPINNER_AND_TEXT = 5; @@ -105,35 +105,20 @@ namespace unity NUX_IMPLEMENT_OBJECT_TYPE(SearchBar); SearchBar::SearchBar(NUX_FILE_LINE_DECL) - : View(NUX_FILE_LINE_PARAM) - , search_hint("") - , showing_filters(false) - , can_refine_search(false) - , show_filter_hint_(true) - , expander_view_(nullptr) - , show_filters_(nullptr) - , last_width_(-1) - , last_height_(-1) -{ - Init(); -} + : SearchBar(false) +{} -SearchBar::SearchBar(bool show_filter_hint_, NUX_FILE_LINE_DECL) +SearchBar::SearchBar(bool show_filter_hint, NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) - , search_hint("") , showing_filters(false) , can_refine_search(false) - , show_filter_hint_(show_filter_hint_) + , live_search_wait(DEFAULT_LIVE_SEARCH_TIMEOUT) + , show_filter_hint_(show_filter_hint) , expander_view_(nullptr) , show_filters_(nullptr) , last_width_(-1) , last_height_(-1) { - Init(); -} - -void SearchBar::Init() -{ dash::Style& style = dash::Style::Instance(); nux::BaseTexture* icon = style.GetSearchMagnifyIcon(); @@ -199,7 +184,7 @@ void SearchBar::Init() expand_icon_ = new IconTexture(arrow, arrow->GetWidth(), arrow->GetHeight()); - expand_icon_->SetOpacity(kExpandDefaultIconOpacity); + expand_icon_->SetOpacity(DEFAULT_ICON_OPACITY); expand_icon_->SetMinimumSize(arrow->GetWidth(), arrow->GetHeight()); expand_icon_->SetVisible(false); @@ -294,10 +279,13 @@ void SearchBar::OnFontChanged(GtkSettings* settings, GParamSpec* pspec) font_desc << pango_font_description_get_family(desc) << " " << HINT_LABEL_FONT_STYLE << " " << HINT_LABEL_FONT_SIZE; hint_->SetFont(font_desc.str().c_str()); - font_desc.str(""); - font_desc.clear(); - font_desc << pango_font_description_get_family(desc) << " " << SHOW_FILTERS_LABEL_FONT_STYLE << " " << SHOW_FILTERS_LABEL_FONT_SIZE; - show_filters_->SetFont(font_desc.str().c_str()); + if (show_filter_hint_) + { + font_desc.str(""); + font_desc.clear(); + font_desc << pango_font_description_get_family(desc) << " " << SHOW_FILTERS_LABEL_FONT_STYLE << " " << SHOW_FILTERS_LABEL_FONT_SIZE; + show_filters_->SetFont(font_desc.str().c_str()); + } pango_font_description_free(desc); } @@ -317,7 +305,7 @@ void SearchBar::OnSearchChanged(nux::TextEntry* text_entry) // We don't want to set a new search string on every new character, so we add a sma // timeout to see if the user is typing a sentence. If more characters are added, we // keep restarting the timeout unti the user has actuay paused. - live_search_timeout_.reset(new glib::Timeout(LIVE_SEARCH_TIMEOUT)); + live_search_timeout_.reset(new glib::Timeout(live_search_wait())); live_search_timeout_->Run(sigc::mem_fun(this, &SearchBar::OnLiveSearchTimeout)); // Don't animate the spinner immediately, the searches are fast and @@ -428,6 +416,9 @@ void SearchBar::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_dra nux::GetPainter().PushPaintLayerStack(); } + if (!IsFullRedraw()) + graphics::ClearGeometry(pango_entry_->GetGeometry()); + layout_->ProcessDraw(graphics_engine, force_draw); if (IsFullRedraw()) @@ -455,7 +446,7 @@ void SearchBar::OnEntryActivated() void SearchBar::ForceLiveSearch() { - live_search_timeout_.reset(new glib::Timeout(LIVE_SEARCH_TIMEOUT)); + live_search_timeout_.reset(new glib::Timeout(live_search_wait())); live_search_timeout_->Run(sigc::mem_fun(this, &SearchBar::OnLiveSearchTimeout)); start_spinner_timeout_.reset(new glib::Timeout(SPINNER_TIMEOUT)); @@ -466,8 +457,7 @@ void SearchBar::SetSearchFinished() { start_spinner_timeout_.reset(); - bool is_empty = pango_entry_->im_active() ? - false : pango_entry_->GetText() == ""; + bool is_empty = pango_entry_->im_active() ? false : pango_entry_->GetText().empty(); spinner_->SetState(is_empty ? STATE_READY : STATE_CLEAR); } @@ -609,14 +599,19 @@ void SearchBar::AddProperties(debug::IntrospectionData& introspection) .add(GetAbsoluteGeometry()) .add("has_focus", pango_entry_->HasKeyFocus()) .add("search_string", pango_entry_->GetText()) - .add("expander-has-focus", expander_view_->HasKeyFocus()) .add("showing-filters", showing_filters) - .add("filter-label-x", show_filters_->GetAbsoluteX()) - .add("filter-label-y", show_filters_->GetAbsoluteY()) - .add("filter-label-width", show_filters_->GetAbsoluteWidth()) - .add("filter-label-height", show_filters_->GetAbsoluteHeight()) - .add("filter-label-geo", show_filters_->GetAbsoluteGeometry()) .add("im_active", pango_entry_->im_active()); + + if (show_filter_hint_) + { + introspection + .add("expander-has-focus", expander_view_->HasKeyFocus()) + .add("filter-label-x", show_filters_->GetAbsoluteX()) + .add("filter-label-y", show_filters_->GetAbsoluteY()) + .add("filter-label-width", show_filters_->GetAbsoluteWidth()) + .add("filter-label-height", show_filters_->GetAbsoluteHeight()) + .add("filter-label-geo", show_filters_->GetAbsoluteGeometry()); + } } } // namespace unity diff --git a/unity-shared/SearchBar.h b/unity-shared/SearchBar.h index 18f8e34a3..e4f1505fa 100644 --- a/unity-shared/SearchBar.h +++ b/unity-shared/SearchBar.h @@ -63,14 +63,13 @@ public: nux::Property<bool> can_refine_search; nux::ROProperty<bool> im_active; nux::ROProperty<bool> im_preedit; + nux::Property<unsigned> live_search_wait; sigc::signal<void> activated; sigc::signal<void, std::string const&> search_changed; sigc::signal<void, std::string const&> live_search_reached; private: - void Init(); - void OnFontChanged(GtkSettings* settings, GParamSpec* pspec=NULL); void OnSearchHintChanged(); diff --git a/unity-shared/SpreadFilter.cpp b/unity-shared/SpreadFilter.cpp new file mode 100644 index 000000000..ddbd99696 --- /dev/null +++ b/unity-shared/SpreadFilter.cpp @@ -0,0 +1,121 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* +* Copyright (C) 2014 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 <marco@ubuntu.com> +*/ + +#include "SpreadFilter.h" + +#include <Nux/HLayout.h> +#include "AnimationUtils.h" +#include "SearchBar.h" +#include "WindowManager.h" + +namespace unity +{ +namespace spread +{ +namespace +{ +const unsigned FADE_DURATION = 100; +const unsigned DEFAULT_SEARCH_WAIT = 300; +const nux::Point OFFSET(10, 15); +const nux::Size SIZE(620, 42); +} + +Filter::Filter() + : fade_animator_(FADE_DURATION) +{ + search_bar_ = SearchBar::Ptr(new SearchBar()); + search_bar_->SetMinMaxSize(SIZE.width, SIZE.height); + search_bar_->live_search_wait = DEFAULT_SEARCH_WAIT; + text.SetGetterFunction([this] { return search_bar_->search_string(); }); + text.SetSetterFunction([this] (std::string const& t) { search_bar_->search_string = t; return false; }); + debug::Introspectable::AddChild(search_bar_.GetPointer()); + + auto layout = new nux::HLayout(NUX_TRACKER_LOCATION); + layout->SetVerticalExternalMargin(0); + layout->SetHorizontalExternalMargin(0); + layout->AddView(search_bar_.GetPointer()); + + auto const& work_area = WindowManager::Default().GetWorkAreaGeometry(0); + view_window_ = new nux::BaseWindow(GetName().c_str()); + view_window_->SetLayout(layout); + view_window_->SetBackgroundColor(nux::color::Transparent); + view_window_->SetWindowSizeMatchLayout(true); + view_window_->ShowWindow(true); + view_window_->PushToFront(); + view_window_->SetOpacity(0.0f); + view_window_->SetEnterFocusInputArea(search_bar_.GetPointer()); + view_window_->SetInputFocus(); + view_window_->SetXY(OFFSET.x + work_area.x, OFFSET.y + work_area.y); + fade_animator_.updated.connect([this] (double opacity) { view_window_->SetOpacity(opacity); }); + + nux::GetWindowCompositor().SetKeyFocusArea(search_bar_->text_entry()); + + search_bar_->search_changed.connect([this] (std::string const& search) { + if (!Visible()) + animation::StartOrReverse(fade_animator_, animation::Direction::FORWARD); + + if (search.empty()) + { + text.changed.emit(search); + animation::StartOrReverse(fade_animator_, animation::Direction::BACKWARD); + } + }); + + search_bar_->live_search_reached.connect([this] (std::string const& search) { + if (!search.empty()) + { + text.changed.emit(search); + search_bar_->SetSearchFinished(); + } + }); +} + +Filter::~Filter() +{ + nux::GetWindowCompositor().SetKeyFocusArea(nullptr); + nux::GetWindowThread()->RemoveObjectFromLayoutQueue(view_window_.GetPointer()); +} + +bool Filter::Visible() const +{ + return (view_window_->GetOpacity() != 0.0f); +} + +nux::Geometry const& Filter::GetAbsoluteGeometry() const +{ + return view_window_->GetGeometry(); +} + +// +// Introspection +// +std::string Filter::GetName() const +{ + return "SpreadFilter"; +} + +void Filter::AddProperties(debug::IntrospectionData& introspection) +{ + introspection + .add(GetAbsoluteGeometry()) + .add("visible", Visible()); +} + +} // namespace spread +} // namespace unity diff --git a/unity-shared/SpreadFilter.h b/unity-shared/SpreadFilter.h new file mode 100644 index 000000000..2709315a5 --- /dev/null +++ b/unity-shared/SpreadFilter.h @@ -0,0 +1,64 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* +* Copyright (C) 2014 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 <marco@ubuntu.com> +*/ + +#ifndef UNITYSHELL_SPREAD_FILTER_H +#define UNITYSHELL_SPREAD_FILTER_H + +#include <memory> + +#include <Nux/Nux.h> +#include <Nux/BaseWindow.h> +#include <NuxCore/Animation.h> +#include "Introspectable.h" + +namespace unity +{ +class SearchBar; + +namespace spread +{ + +class Filter : public debug::Introspectable, public sigc::trackable +{ +public: + typedef std::shared_ptr<Filter> Ptr; + + Filter(); + virtual ~Filter(); + + nux::RWProperty<std::string> text; + + bool Visible() const; + nux::Geometry const& GetAbsoluteGeometry() const; + +protected: + // Introspectable + std::string GetName() const; + void AddProperties(debug::IntrospectionData&); + +private: + nux::ObjectPtr<SearchBar> search_bar_; + nux::ObjectPtr<nux::BaseWindow> view_window_; + nux::animation::AnimateValue<double> fade_animator_; +}; + +} // namespace spread +} // namespace unity + +#endif |
