diff options
| author | Iain Lane <iain@orangesquash.org.uk> | 2015-08-24 17:24:34 +0100 |
|---|---|---|
| committer | Iain Lane <iain@orangesquash.org.uk> | 2015-08-24 17:24:34 +0100 |
| commit | 26717828a63ad3d97dfd6e5a4933a922289db85d (patch) | |
| tree | f518f1cc01e5d2d6979565163e478247fb01d4c1 | |
| parent | 60d495750881ce5833072d23a84028e835bcd616 (diff) | |
| parent | 3dc35ae83a871b47ea25b9f0c9beb18a362e8aee (diff) | |
Merge archive upload
(bzr r3987.2.2)
44 files changed, 482 insertions, 486 deletions
diff --git a/dash/DashView.cpp b/dash/DashView.cpp index 8afde747f..391ffe625 100644 --- a/dash/DashView.cpp +++ b/dash/DashView.cpp @@ -1547,6 +1547,32 @@ void DashView::ProcessDndEnter() ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); } +nux::Area* DashView::SkipUnexpandableHeaderKeyNav() +{ + PlacesGroup::Ptr prev_view; + auto category_views = active_scope_view_->GetOrderedCategoryViews(); + + for (auto category : category_views) + { + if (category->GetLayout() != nullptr) + { + auto header = category->GetHeaderFocusableView(); + if (header && header->HasKeyFocus() && !category->IsExpandable()) + { + if (prev_view) + return prev_view->GetChildView(); + else + return search_bar_->text_entry(); + } + + if (category->IsVisible()) + prev_view = category; + } + } + + return nullptr; +} + nux::Area* DashView::FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state) @@ -1698,6 +1724,14 @@ nux::Area* DashView::FindKeyFocusArea(unsigned int key_symbol, } } + if (direction == KEY_NAV_UP) + { + if (auto skip_view = SkipUnexpandableHeaderKeyNav()) + { + return skip_view; + } + } + bool search_key = false; if (direction == KEY_NAV_NONE) diff --git a/dash/DashView.h b/dash/DashView.h index 5c7e5c0e6..cdcf70767 100644 --- a/dash/DashView.h +++ b/dash/DashView.h @@ -137,6 +137,8 @@ private: nux::Area* KeyNavIteration(nux::KeyNavDirection direction); + nux::Area* SkipUnexpandableHeaderKeyNav(); + UBusManager ubus_manager_; Scopes::Ptr scopes_; ScopeViews scope_views_; diff --git a/dash/PlacesGroup.cpp b/dash/PlacesGroup.cpp index 9d906100f..f0ba9087f 100755 --- a/dash/PlacesGroup.cpp +++ b/dash/PlacesGroup.cpp @@ -212,7 +212,12 @@ PlacesGroup::PlacesGroup(dash::StyleInterface& style) if(direction == nux::KEY_NAV_UP) nux::GetWindowCompositor().SetKeyFocusArea(_child_view, direction); else - nux::GetWindowCompositor().SetKeyFocusArea(GetHeaderFocusableView(), direction); + { + if (IsExpandable()) + nux::GetWindowCompositor().SetKeyFocusArea(GetHeaderFocusableView(), direction); + else + nux::GetWindowCompositor().SetKeyFocusArea(_child_view, direction); + } }); UpdatePlacesGroupSize(); @@ -343,15 +348,18 @@ PlacesGroup::SetChildView(dash::ResultView* view) UpdateResultViewPadding(); _group_layout->AddLayout(_child_layout, 1); - view->results_per_row.changed.connect([this] (int results_per_row) - { - _n_visible_items_in_unexpand_mode = results_per_row; - RefreshLabel(); - }); + UpdateVisibleItems(view->results_per_row()); + view->results_per_row.changed.connect(sigc::mem_fun(this, &PlacesGroup::UpdateVisibleItems)); QueueDraw(); } +void PlacesGroup::UpdateVisibleItems(int visible_items) +{ + _n_visible_items_in_unexpand_mode = visible_items; + RefreshLabel(); +} + dash::ResultView* PlacesGroup::GetChildView() { @@ -567,6 +575,12 @@ PlacesGroup::SetCounts(unsigned n_total_items) } bool +PlacesGroup::IsExpandable() const +{ + return (_n_visible_items_in_unexpand_mode < _n_total_items); +} + +bool PlacesGroup::GetExpanded() const { return _is_expanded; @@ -649,7 +663,7 @@ nux::View* PlacesGroup::GetHeaderFocusableView() const bool PlacesGroup::ShouldBeHighlighted() const { - return HeaderHasKeyFocus(); + return (HeaderHasKeyFocus() && IsExpandable()); } void PlacesGroup::SetResultsPreviewAnimationValue(float preview_animation) diff --git a/dash/PlacesGroup.h b/dash/PlacesGroup.h index d99883703..6b1295b95 100644 --- a/dash/PlacesGroup.h +++ b/dash/PlacesGroup.h @@ -77,6 +77,7 @@ public: void SetCounts(unsigned n_total_items); + virtual bool IsExpandable() const; virtual void SetExpanded(bool is_expanded); virtual bool GetExpanded() const; @@ -127,6 +128,7 @@ private: void UpdatePlacesGroupSize(); void UpdateResultViewPadding(); void UpdateScale(double scale); + void UpdateVisibleItems(int visible_items); private: std::string _category_id; diff --git a/dash/ResultViewGrid.cpp b/dash/ResultViewGrid.cpp index ee3e3c286..78a01de9a 100644 --- a/dash/ResultViewGrid.cpp +++ b/dash/ResultViewGrid.cpp @@ -85,11 +85,11 @@ ResultViewGrid::ResultViewGrid(NUX_FILE_LINE_DECL) EnableDoubleClick(true); SetAcceptKeyNavFocusOnMouseDown(false); - auto needredraw_lambda = [this](int value) { NeedRedraw(); }; - horizontal_spacing.changed.connect(needredraw_lambda); - vertical_spacing.changed.connect(needredraw_lambda); - padding.changed.connect(needredraw_lambda); - selected_index_.changed.connect(needredraw_lambda); + auto queue_draw_cb = sigc::hide(sigc::mem_fun(this, &ResultViewGrid::QueueDraw)); + horizontal_spacing.changed.connect(queue_draw_cb); + vertical_spacing.changed.connect(queue_draw_cb); + padding.changed.connect(queue_draw_cb); + selected_index_.changed.connect(queue_draw_cb); expanded.changed.connect([this](bool value) { if (value) all_results_preloaded_ = false; }); results_per_row.changed.connect([this](int value) { if (value > 0) all_results_preloaded_ = false; }); scale.changed.connect(sigc::mem_fun(this, &ResultViewGrid::UpdateScale)); @@ -119,7 +119,7 @@ ResultViewGrid::ResultViewGrid(NUX_FILE_LINE_DECL) NeedRedraw(); }); - WindowManager::Default().average_color.changed.connect(sigc::hide(sigc::mem_fun(this, &View::QueueDraw))); + WindowManager::Default().average_color.changed.connect(queue_draw_cb); ubus_.RegisterInterest(UBUS_DASH_SIZE_CHANGED, [this] (GVariant* data) { // on dash size changed, we update our stored values, this sucks diff --git a/debian/changelog b/debian/changelog index 468f7fe82..d3fb4e7d0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,32 @@ +unity (7.3.2+15.10.20150819-0ubuntu1) wily; urgency=medium + + [ Andrea Azzarone andrea.azzarone@canonical.com ] + * Do not handle events coming from viewports not actually containing + the window. (LP: #1449654) + + [ Chris Townsend ] + * Also use the Compiz show() method when forcing an unmapped window to + be visible when clicking on it's active Launcher icon. (LP: #989588) + * When using keyboard navigation in the Dash, skip category headers + that are not expandable. Also, do not highlight the category header + when the mouse cursor is over it. (LP: #1045933) + + [ Marco Trevisan (TreviƱo) ] + * ApplicationManager: rely on windows monitor property changes for + updating Pips (LP: #1027191) + * Autopilot: modernize some tests, use stronger methods to ensure + false positive + * DecorationsForceQuitDialog: override the background of the window + with transparent color (LP: #1470292) + * Launcher: Always unfold an active icon (LP: #1472339) + * PanelMenuView: ensure that we connect to window signals as soon as + the AppManager knows it (LP: #1472326) + * PlacesGroup: connect to view changes using a function instead of a + lambda (LP: #1470298) + * SessionButton: set button opacity to 75% when pressed (LP: #1301655) + + -- CI Train Bot <ci-train-bot@canonical.com> Wed, 19 Aug 2015 14:45:18 +0000 + unity (7.3.2+15.10.20150803.1-0ubuntu1) wily; urgency=medium [ Andrea Azzarone andrea.azzarone@canonical.com ] diff --git a/decorations/DecoratedWindow.cpp b/decorations/DecoratedWindow.cpp index ea43f900a..b58a56c5a 100644 --- a/decorations/DecoratedWindow.cpp +++ b/decorations/DecoratedWindow.cpp @@ -842,6 +842,11 @@ Window::Window(CompWindow* cwin) , impl_(new Impl(this, cwin)) {} +CompWindow* Window::GetCompWindow() +{ + return impl_->win_; +} + void Window::Update() { impl_->Update(); diff --git a/decorations/DecoratedWindow.h b/decorations/DecoratedWindow.h index be49be600..893a6e0f3 100644 --- a/decorations/DecoratedWindow.h +++ b/decorations/DecoratedWindow.h @@ -45,6 +45,7 @@ public: nux::Property<bool> scaled; nux::ROProperty<double> dpi_scale; + CompWindow* GetCompWindow(); void Update(); void Undecorate(); void UpdateDecorationPosition(); diff --git a/decorations/DecorationsForceQuitDialog.cpp b/decorations/DecorationsForceQuitDialog.cpp index 496803b0a..98b6349d7 100644 --- a/decorations/DecorationsForceQuitDialog.cpp +++ b/decorations/DecorationsForceQuitDialog.cpp @@ -169,6 +169,14 @@ GtkWidget* sheet_style_window_new(ForceQuitDialog* main_dialog, Window parent_xi gtk_widget_set_visual(GTK_WIDGET(self), gdk_screen_get_rgba_visual(screen)); gtk_widget_realize(GTK_WIDGET(self)); + glib::Object<GtkCssProvider> style(gtk_css_provider_new()); + gtk_css_provider_load_from_data(style, R"( + * { background-color: transparent; } + )", -1, nullptr); + + auto* style_ctx = gtk_widget_get_style_context(GTK_WIDGET(self)); + gtk_style_context_add_provider(style_ctx, glib::object_cast<GtkStyleProvider>(style), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + gtk_container_add(GTK_CONTAINER(self), sheet_style_dialog_new(main_dialog, parent_xid, parent_pid)); gtk_window_set_modal(self, TRUE); diff --git a/decorations/DecorationsManager.cpp b/decorations/DecorationsManager.cpp index 1b90d885f..e859850c9 100644 --- a/decorations/DecorationsManager.cpp +++ b/decorations/DecorationsManager.cpp @@ -172,7 +172,7 @@ bool Manager::Impl::UpdateWindow(::Window xid) { auto const& win = GetWindowByXid(xid); - if (win && !win->impl_->win_->hasUnmapReference()) + if (win && !win->GetCompWindow()->hasUnmapReference()) { win->Update(); return true; @@ -306,6 +306,10 @@ bool Manager::Impl::HandleFrameEvent(XEvent* event) return false; auto const& win = GetWindowByFrame(event->xany.window); + CompWindow* comp_window = win ? win->GetCompWindow() : nullptr; + + if (comp_window && comp_window->defaultViewport() != screen->vp()) + return false; // ButtonRelease events might happen also outside the frame window, in this // case we must unset the mouse owner, wherever the event happens. diff --git a/launcher/ApplicationLauncherIcon.cpp b/launcher/ApplicationLauncherIcon.cpp index e83621bdd..9010f3c02 100644 --- a/launcher/ApplicationLauncherIcon.cpp +++ b/launcher/ApplicationLauncherIcon.cpp @@ -48,7 +48,6 @@ DECLARE_LOGGER(logger, "unity.launcher.icon.application"); namespace { // We use the "bamf-" prefix since the manager is protected, to avoid name clash -const std::string WINDOW_MOVE_TIMEOUT = "bamf-window-move"; const std::string ICON_REMOVE_TIMEOUT = "bamf-icon-remove"; const std::string ICON_DND_OVER_TIMEOUT = "bamf-icon-dnd-over"; const std::string DEFAULT_ICON = "application-default-icon"; @@ -87,12 +86,11 @@ ApplicationLauncherIcon::ApplicationLauncherIcon(ApplicationPtr const& app) WindowManager& wm = WindowManager::Default(); wm.window_minimized.connect(sigc::mem_fun(this, &ApplicationLauncherIcon::OnWindowMinimized)); - wm.window_moved.connect(sigc::mem_fun(this, &ApplicationLauncherIcon::OnWindowMoved)); wm.screen_viewport_switch_ended.connect(sigc::mem_fun(this, &ApplicationLauncherIcon::EnsureWindowState)); wm.terminate_expo.connect(sigc::mem_fun(this, &ApplicationLauncherIcon::EnsureWindowState)); - UScreen::GetDefault()->changed.connect(sigc::hide(sigc::hide(sigc::mem_fun(this, &ApplicationLauncherIcon::EnsureWindowState)))); + UScreen::GetDefault()->changed.connect(sigc::hide(sigc::hide(sigc::mem_fun(this, &ApplicationLauncherIcon::EnsureWindowsLocation)))); - EnsureWindowState(); + EnsureWindowsLocation(); } ApplicationLauncherIcon::~ApplicationLauncherIcon() @@ -156,14 +154,16 @@ void ApplicationLauncherIcon::SetupApplicationSignalsConnections() { // Lambda functions should be fine here because when the application the icon // is only ever removed when the application is closed. - signals_conn_.Add(app_->window_opened.connect([this](ApplicationWindowPtr const&) { - EnsureWindowState(); - UpdateIconGeometries(GetCenters()); + signals_conn_.Add(app_->window_opened.connect([this](ApplicationWindowPtr const& win) { + signals_conn_.Add(win->monitor.changed.connect([this] (int) { EnsureWindowsLocation(); })); + EnsureWindowsLocation(); })); - auto ensure_windows_cb = sigc::hide(sigc::mem_fun(this, &ApplicationLauncherIcon::EnsureWindowState)); - signals_conn_.Add(app_->window_closed.connect(ensure_windows_cb)); - signals_conn_.Add(app_->window_moved.connect(ensure_windows_cb)); + auto ensure_win_location_cb = sigc::hide(sigc::mem_fun(this, &ApplicationLauncherIcon::EnsureWindowsLocation)); + signals_conn_.Add(app_->window_closed.connect(ensure_win_location_cb)); + + for (auto& win : app_->GetWindows()) + signals_conn_.Add(win->monitor.changed.connect(ensure_win_location_cb)); signals_conn_.Add(app_->urgent.changed.connect([this](bool const& urgent) { LOG_DEBUG(logger) << tooltip_text() << " urgent now " << (urgent ? "true" : "false"); @@ -535,19 +535,6 @@ void ApplicationLauncherIcon::OnWindowMinimized(guint32 xid) } } -void ApplicationLauncherIcon::OnWindowMoved(guint32 moved_win) -{ - if (!app_->OwnsWindow(moved_win)) - return; - - _source_manager.AddTimeout(250, [this] { - EnsureWindowState(); - UpdateIconGeometries(GetCenters()); - - return false; - }, WINDOW_MOVE_TIMEOUT); -} - void ApplicationLauncherIcon::UpdateDesktopFile() { std::string const& filename = app_->desktop_file(); @@ -738,8 +725,8 @@ void ApplicationLauncherIcon::EnsureWindowState() // If monitor is -1 (or negative), show on all monitors. if (monitor < 0) { - for (unsigned j; j < monitors::MAX; j++) - ++number_of_windows_on_monitor[j]; + for (unsigned j; j < monitors::MAX; ++j) + ++number_of_windows_on_monitor[j]; } else { @@ -748,12 +735,18 @@ void ApplicationLauncherIcon::EnsureWindowState() } } - for (unsigned i = 0; i < monitors::MAX; i++) + for (unsigned i = 0; i < monitors::MAX; ++i) SetNumberOfWindowsVisibleOnMonitor(number_of_windows_on_monitor[i], i); WindowsChanged.emit(); } +void ApplicationLauncherIcon::EnsureWindowsLocation() +{ + EnsureWindowState(); + UpdateIconGeometries(GetCenters()); +} + void ApplicationLauncherIcon::UpdateDesktopQuickList() { std::string const& desktop_file = DesktopFile(); diff --git a/launcher/ApplicationLauncherIcon.h b/launcher/ApplicationLauncherIcon.h index b92ad5e1f..2ea48c184 100644 --- a/launcher/ApplicationLauncherIcon.h +++ b/launcher/ApplicationLauncherIcon.h @@ -116,6 +116,7 @@ private: void UnsetApplication(); void SetupApplicationSignalsConnections(); void EnsureWindowState(); + void EnsureWindowsLocation(); void EnsureMenuItemsWindowsReady(); void EnsureMenuItemsDefaultReady(); void EnsureMenuItemsStaticQuicklist(); @@ -127,7 +128,6 @@ private: bool Spread(bool current_desktop, int state, bool force); void OnWindowMinimized(guint32 xid); - void OnWindowMoved(guint32 xid); WindowList GetWindows(WindowFilterMask filter = 0, int monitor = -1); const std::set<std::string> GetSupportedTypes(); diff --git a/launcher/Launcher.cpp b/launcher/Launcher.cpp index 1ea95e109..dc6b725ca 100644 --- a/launcher/Launcher.cpp +++ b/launcher/Launcher.cpp @@ -677,7 +677,9 @@ void Launcher::FillRenderArg(AbstractLauncherIcon::Ptr const& icon, // goes for 0.0f when fully unfolded, to 1.0f folded float folding_progress = CLAMP((center.y + c_icon_size - folding_threshold) / (float) c_icon_size, 0.0f, 1.0f); float unfold_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::UNFOLDED, monitor()); + float active_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::ACTIVE, monitor()); + unfold_progress = CLAMP(unfold_progress + active_progress, 0.0f, 1.0f); folding_progress *= 1.0f - unfold_progress; float half_size = (folded_size / 2.0f) + (c_icon_size / 2.0f - folded_size / 2.0f) * (1.0f - folding_progress); @@ -779,9 +781,12 @@ void Launcher::RenderArgs(std::list<RenderArg> &launcher_args, sum += height; // magic constant must some day be explained, for now suffice to say this constant prevents the bottom from "marching"; - float magic_constant = 1.3f; + const float magic_constant = 1.3f; float unfold_progress = (*it)->GetQuirkProgress(AbstractLauncherIcon::Quirk::UNFOLDED, monitor()); + float active_progress = (*it)->GetQuirkProgress(AbstractLauncherIcon::Quirk::ACTIVE, monitor()); + + unfold_progress = CLAMP(unfold_progress + active_progress, 0.0f, 1.0f); folding_threshold -= CLAMP(sum - launcher_height, 0.0f, height * magic_constant) * (folding_constant + (1.0f - folding_constant) * unfold_progress); } @@ -1597,6 +1602,7 @@ void Launcher::SetupIconAnimations(AbstractLauncherIcon::Ptr const& icon) { icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::VISIBLE, ANIM_DURATION_SHORT, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::RUNNING, ANIM_DURATION_SHORT, monitor()); + icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::ACTIVE, ANIM_DURATION_SHORT, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::STARTING, (ANIM_DURATION_LONG * MAX_STARTING_BLINKS * STARTING_BLINK_LAMBDA * 2), monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::PULSE_ONCE, (ANIM_DURATION_LONG * PULSE_BLINK_LAMBDA * 2), monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::PRESENTED, ANIM_DURATION, monitor()); diff --git a/panel/PanelMenuView.cpp b/panel/PanelMenuView.cpp index 3ce44613a..56d8a397b 100644 --- a/panel/PanelMenuView.cpp +++ b/panel/PanelMenuView.cpp @@ -146,6 +146,8 @@ void PanelMenuView::SetupPanelMenuViewSignals() am.active_application_changed.connect(sigc::mem_fun(this, &PanelMenuView::OnActiveAppChanged)); am.application_started.connect(sigc::mem_fun(this, &PanelMenuView::OnApplicationStarted)); am.application_stopped.connect(sigc::mem_fun(this, &PanelMenuView::OnApplicationClosed)); + am.window_opened.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowOpened)); + am.window_closed.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowClosed)); mouse_enter.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseEnter)); mouse_leave.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseLeave)); @@ -1028,6 +1030,19 @@ void PanelMenuView::OnApplicationClosed(ApplicationPtr const& app) } } +void PanelMenuView::OnWindowOpened(ApplicationWindowPtr const& win) +{ + if (win->window_id() == window_buttons_->controlled_window() && + win->title.changed.empty()) + { + /* This is a not so nice workaround that we need to include here, since + * BAMF might be late in informing us about a new window, and thus we + * can't connect to it's signals (as not available in the App Manager). */ + window_buttons_->controlled_window = 0; + UpdateTargetWindowItems(); + } +} + void PanelMenuView::OnWindowClosed(ApplicationWindowPtr const& win) { /* FIXME, this can be removed when window_unmapped WindowManager signal diff --git a/panel/PanelMenuView.h b/panel/PanelMenuView.h index ec0ce561a..7be902e85 100644 --- a/panel/PanelMenuView.h +++ b/panel/PanelMenuView.h @@ -89,6 +89,7 @@ private: void OnEntryViewAdded(PanelIndicatorEntryView* view); void OnApplicationStarted(ApplicationPtr const&); void OnApplicationClosed(ApplicationPtr const&); + void OnWindowOpened(ApplicationWindowPtr const&); void OnWindowClosed(ApplicationWindowPtr const&); void OnActiveWindowChanged(ApplicationWindowPtr const&); void OnActiveAppChanged(ApplicationPtr const&); diff --git a/shutdown/SessionButton.cpp b/shutdown/SessionButton.cpp index 06a8afa04..2e0eac9f9 100644 --- a/shutdown/SessionButton.cpp +++ b/shutdown/SessionButton.cpp @@ -40,6 +40,7 @@ Button::Button(Action action, NUX_FILE_LINE_DECL) : nux::View(NUX_FILE_LINE_PARAM) , scale(1.0) , highlighted(false) + , pressed(false) , action([this] { return action_; }) , label([this] { return label_view_->GetText(); }) , action_(action) @@ -107,6 +108,8 @@ Button::Button(Action action, NUX_FILE_LINE_DECL) mouse_enter.connect([this] (int, int, unsigned long, unsigned long) { highlighted = true; }); mouse_leave.connect([this] (int, int, unsigned long, unsigned long) { highlighted = false; }); mouse_click.connect([this] (int, int, unsigned long, unsigned long) { activated.emit(); }); + mouse_down.connect([this] (int, int, unsigned long, unsigned long) { pressed = true; }); + mouse_up.connect([this] (int, int, unsigned long, unsigned long) { pressed = false; }); begin_key_focus.connect([this] { highlighted = true; }); end_key_focus.connect([this] { highlighted = false; }); @@ -116,6 +119,10 @@ Button::Button(Action action, NUX_FILE_LINE_DECL) image_view_->SetTexture(value ? highlight_tex_ : normal_tex_); label_view_->SetTextColor(value ? nux::color::White : nux::color::Transparent); }); + + pressed.changed.connect([this] (bool value) { + image_view_->SetOpacity(value ? 0.75 : 1.0); + }); } void Button::UpdateTextures(std::string const& texture_prefix) diff --git a/shutdown/SessionButton.h b/shutdown/SessionButton.h index a25a65cdc..493113e8a 100644 --- a/shutdown/SessionButton.h +++ b/shutdown/SessionButton.h @@ -52,6 +52,7 @@ public: nux::Property<double> scale; nux::Property<bool> highlighted; + nux::Property<bool> pressed; nux::ROProperty<Action> action; nux::ROProperty<std::string> label; diff --git a/shutdown/SessionView.cpp b/shutdown/SessionView.cpp index 64aa4e96f..70534f0e7 100644 --- a/shutdown/SessionView.cpp +++ b/shutdown/SessionView.cpp @@ -236,7 +236,7 @@ void View::PopulateButtons() { if (mode() == Mode::FULL) { - if (manager_->CanLock()) + if (manager_->CanLock() && !manager_->is_locked()) { auto* button = new Button(Button::Action::LOCK, NUX_TRACKER_LOCATION); button->activated.connect(sigc::mem_fun(manager_.get(), &Manager::LockScreen)); diff --git a/tests/autopilot/unity/emulators/__init__.py b/tests/autopilot/unity/emulators/__init__.py index 998172b80..f10173c37 100644 --- a/tests/autopilot/unity/emulators/__init__.py +++ b/tests/autopilot/unity/emulators/__init__.py @@ -28,6 +28,30 @@ class UnityIntrospectionObject(CustomEmulatorBase): _Backend = DBusAddress.SessionBus(DBUS_SERVICE, DBUS_OBJECT) + def _repr_string(self, obj_details=""): + geostr = "" + if hasattr(self, 'globalRect'): + geostr = " geo=[{r.x}x{r.y} {r.width}x{r.height}]".format(r=self.globalRect) + + obj_details.strip() + obj_details = " "+obj_details if len(obj_details) else "" + + return "<{cls} {addr} id={id}{geo}{details}>".format(cls=self.__class__.__name__, + addr=hex(id(self)), + id=self.id, + geo=geostr, + details=obj_details) + + def __repr__(self): + with self.no_automatic_refreshing(): + return self._repr_string() + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.id == other.id + + def __ne__(self, other): + return not self.__eq__(other) + def ensure_unity_is_running(timeout=300): """Poll the unity debug interface, and return when it's ready for use. diff --git a/tests/autopilot/unity/emulators/dash.py b/tests/autopilot/unity/emulators/dash.py index 58ba69790..8511d64a9 100644 --- a/tests/autopilot/unity/emulators/dash.py +++ b/tests/autopilot/unity/emulators/dash.py @@ -158,7 +158,7 @@ class DashController(UnityIntrospectionObject, KeybindingsHelper): @property def geometry(self): - return (self.view.x, self.view.y, self.view.width, self.view.height) + return self.view.globalRect class DashView(UnityIntrospectionObject): @@ -225,10 +225,12 @@ class ScopeBarIcon(UnityIntrospectionObject): class ScopeView(UnityIntrospectionObject): """A Scope View.""" - def get_groups(self): + def get_categories(self, only_visible=False): """Get a list of all groups within this scopeview. May return an empty list.""" - groups = self.get_children_by_type(PlacesGroup) - return groups + if only_visible: + return self.get_children_by_type(PlacesGroup, is_visible=True) + + return self.get_children_by_type(PlacesGroup) def get_focused_category(self): """Return a PlacesGroup instance for the category whose header has keyboard focus. @@ -236,30 +238,22 @@ class ScopeView(UnityIntrospectionObject): Returns None if no category headers have keyboard focus. """ - categories = self.get_children_by_type(PlacesGroup) - matches = [m for m in categories if m.header_has_keyfocus] - if matches: - return matches[0] - return None + matches = self.get_children_by_type(PlacesGroup, header_has_keyfocus=True) + return matches[0] if matches else None def get_category_by_name(self, category_name): """Return a PlacesGroup instance with the given name, or None.""" - categories = self.get_children_by_type(PlacesGroup) - matches = [m for m in categories if m.name == category_name] - if matches: - return matches[0] - return None + matches = self.get_children_by_type(PlacesGroup, name=category_name) + return matches[0] if matches else None def get_num_visible_categories(self): """Get the number of visible categories in this scope.""" - return len([c for c in self.get_children_by_type(PlacesGroup) if c.is_visible]) + return len(self.get_categories(only_visible=True)) def get_filterbar(self): """Get the filter bar for the current scope, or None if it doesn't have one.""" bars = self.get_children_by_type(FilterBar) - if bars: - return bars[0] - return None + return bars[0] if bars else None class PlacesGroup(UnityIntrospectionObject): @@ -279,26 +273,16 @@ class Result(UnityIntrospectionObject): """A single result in the dash.""" def activate(self, double_click=True): - tx = self.x + (self.width / 2) - ty = self.y + (self.height / 2) m = Mouse.create() - m.move(tx, ty) - m.click(1) + m.click_object(self, button=1) if double_click: - m.click(1) + m.click_object(self, button=1) def preview(self, button=1): - tx = self.x + (self.width / 2) - ty = self.y + (self.height / 2) - m = Mouse.create() - m.move(tx, ty) - m.click(button) + Mouse.create().click_object(self, button) def preview_key(self): - tx = self.x + (self.width / 2) - ty = self.y + (self.height / 2) - m = Mouse.create() - m.move(tx, ty) + Mouse.create().move_to_object(self) k = Keyboard.create() k.press_and_release('Menu') @@ -313,11 +297,8 @@ class FilterBar(UnityIntrospectionObject): def get_focused_filter(self): """Returns the id of the focused filter widget.""" - filters = self.get_children_by_type(FilterExpanderLabel) - for filter_label in filters: - if filter_label.expander_has_focus: - return filter_label - return None + filters = self.get_children_by_type(FilterExpanderLabel, expander_has_focus=True) + return filters[0] if filters else None @property def expanded(self): @@ -366,21 +347,13 @@ class FilterExpanderLabel(UnityIntrospectionObject): def ensure_expanded(self): """Expand the filter expander label, if it's not already""" if not self.expanded: - tx = self.x + self.width / 2 - ty = self.y + self.height / 2 - m = Mouse.create() - m.move(tx, ty) - m.click() + Mouse.create().click_object(self) self.expanded.wait_for(True) def ensure_collapsed(self): """Collapse the filter expander label, if it's not already""" if self.expanded: - tx = self.x + self.width / 2 - ty = self.y + self.height / 2 - m = Mouse.create() - m.move(tx, ty) - m.click() + Mouse.create().click_object(self) self.expanded.wait_for(False) @@ -402,21 +375,14 @@ class Preview(UnityIntrospectionObject): def get_action_by_id(self, action_id): """Returns the action given it's action hint.""" - actions = self.get_children_by_type(ActionButton) - for action in actions: - if action.action == action_id: - return action - return None + actions = self.get_children_by_type(ActionButton, action=action_id) + return actions[0] if actions else None def execute_action_by_id(self, action_id): """Executes an action given by the id.""" action = self.get_action_by_id(action_id) if action: - tx = action.x + (action.width / 2) - ty = action.y + (action.height / 2) - m = Mouse.create() - m.move(tx, ty) - m.click() + Mouse.create().click_object(action) @property def cover_art(self): @@ -495,12 +461,8 @@ class PreviewContainer(UnityIntrospectionObject): def navigate_left(self, count=1): """Navigate preview left""" - navigator = self.get_left_navigator() - - tx = navigator.button_x + (navigator.button_width / 2) - ty = navigator.button_y + (navigator.button_height / 2) m = Mouse.create() - m.move(tx, ty) + m.move_to_object(self.get_left_navigator().button_geo) old_preview_initiate_count = self.preview_initiate_count @@ -512,12 +474,8 @@ class PreviewContainer(UnityIntrospectionObject): def navigate_right(self, count=1): """Navigate preview right""" - navigator = self.get_right_navigator() - - tx = navigator.button_x + (navigator.button_width / 2) - ty = navigator.button_y + (navigator.button_height / 2) m = Mouse.create() - m.move(tx, ty) + m.move_to_object(self.get_right_navigator().button_geo) old_preview_initiate_count = self.preview_initiate_count @@ -607,3 +565,4 @@ class IconTexture(UnityIntrospectionObject): class StaticCairoText(UnityIntrospectionObject): """Text boxes in the preview""" + diff --git a/tests/autopilot/unity/emulators/hud.py b/tests/autopilot/unity/emulators/hud.py index bee29f54b..81e184c35 100644 --- a/tests/autopilot/unity/emulators/hud.py +++ b/tests/autopilot/unity/emulators/hud.py @@ -105,7 +105,7 @@ class HudController(UnityIntrospectionObject, KeybindingsHelper): @property def geometry(self): - return (self.x, self.y, self.width, self.height) + return self.globalRect @property def selected_button(self): @@ -156,7 +156,7 @@ class HudView(UnityIntrospectionObject): @property def geometry(self): - return (self.x, self.y, self.width, self.height) + return self.globalRect class HudButton(UnityIntrospectionObject): diff --git a/tests/autopilot/unity/emulators/icons.py b/tests/autopilot/unity/emulators/icons.py index 86cddfb0a..8230194dc 100644 --- a/tests/autopilot/unity/emulators/icons.py +++ b/tests/autopilot/unity/emulators/icons.py @@ -58,10 +58,6 @@ class SimpleLauncherIcon(UnityIntrospectionObject): return self.xids.contains(xid) - def __repr__(self): - with self.no_automatic_refreshing(): - return "<%s id=%d>" % (self.__class__.__name__, self.id) - class BFBLauncherIcon(SimpleLauncherIcon): """Represents the BFB button in the launcher.""" @@ -80,10 +76,7 @@ class ApplicationLauncherIcon(SimpleLauncherIcon): def __repr__(self): with self.no_automatic_refreshing(): - return "<%s %s id=%d>" % ( - self.__class__.__name__, - self.desktop_id, - self.id) + return self._repr_string("{0.desktop_id}".format(self)) class TrashLauncherIcon(SimpleLauncherIcon): """Represents the trash launcher icon.""" @@ -110,7 +103,7 @@ class HudEmbeddedIcon(UnityIntrospectionObject): @property def geometry(self): - return (self.x, self.y, self.width, self.height) + return self.globalRect class LauncherEntry(UnityIntrospectionObject): diff --git a/tests/autopilot/unity/emulators/launcher.py b/tests/autopilot/unity/emulators/launcher.py index 5deb2fa18..361373395 100644 --- a/tests/autopilot/unity/emulators/launcher.py +++ b/tests/autopilot/unity/emulators/launcher.py @@ -118,12 +118,8 @@ class Launcher(UnityIntrospectionObject, KeybindingsHelper): def move_mouse_over_launcher(self): """Move the mouse over this launcher.""" move_mouse_to_screen(self.monitor) - (x, y, w, h) = self.geometry - target_x = x + w / 2 - target_y = y + h / 2 - logger.debug("Moving mouse to center of launcher.") - self._mouse.move(target_x, target_y) + self._mouse.move_to_object(self) def move_mouse_to_icon(self, icon, autoscroll_offset=0): """Move the mouse to a specific icon.""" @@ -438,8 +434,8 @@ class Launcher(UnityIntrospectionObject, KeybindingsHelper): @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the current launcher.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the current launcher.""" + return self.globalRect class LauncherModel(UnityIntrospectionObject): diff --git a/tests/autopilot/unity/emulators/panel.py b/tests/autopilot/unity/emulators/panel.py index 5c196f6ca..78b10859d 100644 --- a/tests/autopilot/unity/emulators/panel.py +++ b/tests/autopilot/unity/emulators/panel.py @@ -14,6 +14,7 @@ from time import sleep from autopilot.input import Mouse from autopilot.keybindings import KeybindingsHelper +from autopilot.introspection.types import Rectangle from unity.emulators import UnityIntrospectionObject logger = logging.getLogger(__name__) @@ -107,30 +108,18 @@ class UnityPanel(UnityIntrospectionObject, KeybindingsHelper): def move_mouse_over_grab_area(self): """Move the mouse over the grab area for this panel.""" - (x, y, w, h) = self.grab_area.geometry - target_x = x + w / 2 - target_y = y + h / 2 - logger.debug("Moving mouse to center of grab area.") - self._mouse.move(target_x, target_y) + self._mouse.move_to_object(self.grab_area) def move_mouse_over_window_buttons(self): """Move the mouse over the center of the window buttons area for this panel.""" - (x, y, w, h) = self.window_buttons.geometry - target_x = x + w / 2 - target_y = y + h / 2 - logger.debug("Moving mouse to center of the window buttons.") - self._mouse.move(target_x, target_y) + self._mouse.move_to_object(self.window_buttons) def move_mouse_over_indicators(self): """Move the mouse over the center of the indicators area for this panel.""" - (x, y, w, h) = self.indicators.geometry - target_x = x + w / 2 - target_y = y + h / 2 - logger.debug("Moving mouse to center of the indicators area.") - self._mouse.move(target_x, target_y) + self._mouse.move_to_object(self.indicators) def get_indicator_entries(self, visible_only=True, include_hidden_menus=False): """Returns a list of entries for this panel including both menus and indicators""" @@ -192,8 +181,8 @@ class UnityPanel(UnityIntrospectionObject, KeybindingsHelper): @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the current panel.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the current panel.""" + return self.globalRect class MenuView(UnityIntrospectionObject): @@ -215,8 +204,8 @@ class MenuView(UnityIntrospectionObject): @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the current menu view.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the current menu view.""" + return self.globalRect class WindowButtons(UnityIntrospectionObject): @@ -256,8 +245,8 @@ class WindowButtons(UnityIntrospectionObject): @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the current panel.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the current panel.""" + return self.globalRect class WindowButton(UnityIntrospectionObject): @@ -268,20 +257,25 @@ class WindowButton(UnityIntrospectionObject): self._mouse = Mouse.create() def mouse_move_to(self): - target_x = self.x + self.width / 2 - target_y = self.y + self.height / 2 - self._mouse.move(target_x, target_y, rate=20, time_between_events=0.005) + self._mouse.move_to_object(self) def mouse_click(self): - self.mouse_move_to() - sleep(.2) - self._mouse.click(press_duration=.1) + # Ignore buttons that are placed at 0x0, as they're invisible yet + if not self.x and not self.y and not self.visible: + return + + self._mouse.click_object(self) sleep(.01) @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the window button.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the window button.""" + return self.globalRect + + def __repr__(self): + with self.no_automatic_refreshing(): + details = "type={0.type} state={0.visual_state} sensitive={0.sensitive}".format(self) + return self._repr_string(details) class GrabArea(UnityIntrospectionObject): @@ -289,8 +283,8 @@ class GrabArea(UnityIntrospectionObject): @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the grab area.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the grab area.""" + return self.globalRect class Indicators(UnityIntrospectionObject): @@ -314,8 +308,8 @@ class Indicators(UnityIntrospectionObject): @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the indicators area.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the indicators area.""" + return self.globalRect class IndicatorEntry(UnityIntrospectionObject): @@ -326,30 +320,26 @@ class IndicatorEntry(UnityIntrospectionObject): self._mouse = Mouse.create() def mouse_move_to(self): - target_x = self.x + self.width / 2 - target_y = self.y + self.height / 2 - self._mouse.move(target_x, target_y, rate=20, time_between_events=0.005) + self._mouse.move_to_object(self) def mouse_click(self, button=1): - self.mouse_move_to() - sleep(.2) - assert(self.visible) - self._mouse.click(press_duration=.1) + self._mouse.click_object(self, button=button) sleep(.01) @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the indicator entry.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the indicator entry.""" + return self.globalRect @property def menu_geometry(self): - """Returns a tuple of (x,y,w,h) for the opened menu geometry.""" - return (self.menu_x, self.menu_y, self.menu_width, self.menu_height) + """Returns a Rectangle (x,y,w,h) for the opened menu geometry.""" + return Rectangle(self.menu_x, self.menu_y, self.menu_width, self.menu_height) def __repr__(self): with self.no_automatic_refreshing(): - return "<IndicatorEntry 0x%x (%s)>" % (id(self), self.label) + details = "label={0.label}".format(self) + return self._repr_string(details) class Tray(UnityIntrospectionObject): diff --git a/tests/autopilot/unity/emulators/quicklist.py b/tests/autopilot/unity/emulators/quicklist.py index a3e522600..42601bd3d 100644 --- a/tests/autopilot/unity/emulators/quicklist.py +++ b/tests/autopilot/unity/emulators/quicklist.py @@ -66,8 +66,8 @@ class Quicklist(UnityIntrospectionObject): @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the quicklist.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the quicklist.""" + return self.globalRect class QuicklistMenuItem(UnityIntrospectionObject): @@ -79,20 +79,17 @@ class QuicklistMenuItem(UnityIntrospectionObject): @property def geometry(self): - """Returns a tuple of (x,y,w,h) for the quicklist item.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the quicklist item.""" + return self.globalRect def mouse_move_to(self): assert(self.visible) logger.debug("Moving mouse over quicklist item %r", self) - target_x = self.x + self.width / 2 - target_y = self.y + self.height / 2 - self._mouse.move(target_x, target_y, rate=20, time_between_events=0.005) + self._mouse.move_to_object(self) def mouse_click(self, button=1): logger.debug("Clicking on quicklist item %r", self) - self.mouse_move_to() - self._mouse.click() + self._mouse.click_object(self) class QuicklistMenuItemLabel(QuicklistMenuItem): diff --git a/tests/autopilot/unity/emulators/screen.py b/tests/autopilot/unity/emulators/screen.py index 18dc90cc6..b9299a2b6 100644 --- a/tests/autopilot/unity/emulators/screen.py +++ b/tests/autopilot/unity/emulators/screen.py @@ -13,6 +13,7 @@ import logging from unity.emulators import UnityIntrospectionObject from testtools.matchers import GreaterThan +from autopilot.introspection.types import Rectangle from unity.emulators.dash import SearchBar logger = logging.getLogger(__name__) @@ -54,15 +55,15 @@ class Window(UnityIntrospectionObject): @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) + """Returns a Rectangle (x,y,w,h) for the current window.""" + return self.globalRect @property def scale_close_geometry(self): - """Returns a tuple of (x,y,w,h) for the scale close button.""" + """Returns a Rectangle (x,y,w,h) for the scale close button.""" 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) + return Rectangle(self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height) class SpreadFilter(UnityIntrospectionObject): diff --git a/tests/autopilot/unity/emulators/window_manager.py b/tests/autopilot/unity/emulators/window_manager.py index 4ce550386..d07fec8ed 100644 --- a/tests/autopilot/unity/emulators/window_manager.py +++ b/tests/autopilot/unity/emulators/window_manager.py @@ -10,6 +10,7 @@ from __future__ import absolute_import import logging +from autopilot.introspection.types import Rectangle from autopilot.keybindings import KeybindingsHelper from unity.emulators import UnityIntrospectionObject @@ -22,8 +23,8 @@ class WindowManager(UnityIntrospectionObject, KeybindingsHelper): @property def screen_geometry(self): - """Returns a tuple of (x,y,w,h) for the screen.""" - return (self.x, self.y, self.width, self.height) + """Returns a Rectangle (x,y,w,h) for the screen.""" + return self.globalRect def initiate_spread(self): self.keybinding("spread/start") diff --git a/tests/autopilot/unity/tests/launcher/test_icon_behavior.py b/tests/autopilot/unity/tests/launcher/test_icon_behavior.py index 629e6ef3d..adadfbf91 100644 --- a/tests/autopilot/unity/tests/launcher/test_icon_behavior.py +++ b/tests/autopilot/unity/tests/launcher/test_icon_behavior.py @@ -384,7 +384,7 @@ class LauncherDragIconsBehavior(LauncherTestCase): self.drag_type) moved_icon = self.unity.launcher.model.\ get_launcher_icons_for_monitor(self.launcher_monitor)[1] - self.assertThat(moved_icon.id, Equals(calc_icon.id)) + self.assertThat(moved_icon, Equals(calc_icon)) def test_can_drag_icon_below_window_switcher(self): """Application icons must be dragable to below the workspace switcher icon.""" diff --git a/tests/autopilot/unity/tests/launcher/test_visual.py b/tests/autopilot/unity/tests/launcher/test_visual.py index f7dc8b5b5..c44e9237d 100644 --- a/tests/autopilot/unity/tests/launcher/test_visual.py +++ b/tests/autopilot/unity/tests/launcher/test_visual.py @@ -71,14 +71,12 @@ class LauncherVisualTests(LauncherTestCase): def test_mouse_over_with_dash_open_desaturates_icons(self): """Moving mouse over launcher with dash open must saturate icons.""" - launcher_instance = self.get_launcher() self.unity.dash.ensure_visible() current_monitor = self.unity.dash.monitor self.addCleanup(self.unity.dash.ensure_hidden) sleep(.5) - x,y,w,h = launcher_instance.geometry - self.mouse.move(x + w/2, y + h/2) + self.mouse.move_to_object(self.get_launcher()) sleep(.5) for icon in self.unity.launcher.model.get_launcher_icons(): self.assertFalse(icon.monitors_desaturated[current_monitor]) diff --git a/tests/autopilot/unity/tests/test_dash.py b/tests/autopilot/unity/tests/test_dash.py index 56b40cb09..26fb000f7 100644 --- a/tests/autopilot/unity/tests/test_dash.py +++ b/tests/autopilot/unity/tests/test_dash.py @@ -400,10 +400,11 @@ class DashKeyNavTests(DashTestCase): # Test that tab cycles through the categories. # + 1 is the filter bar - for i in range(scope.get_num_visible_categories()): + for category in scope.get_categories(only_visible=True): self.keyboard.press_and_release('Tab') - category = scope.get_focused_category() - self.assertIsNot(category, None) + selected = scope.get_focused_category() + expected = category if category.expand_label_is_visible else None + self.assertEqual(selected, expected) def test_tab_with_filter_bar(self): """ This test makes sure that Tab works well with the filter bara.""" @@ -544,10 +545,7 @@ class DashClipboardTests(DashTestCase): self.unity.dash.ensure_visible() - self.mouse.move(self.unity.dash.searchbar.x + self.unity.dash.searchbar.width / 2, - self.unity.dash.searchbar.y + self.unity.dash.searchbar.height / 2) - - self.mouse.click(button=2) + self.mouse.click_object(self.unity.dash.searchbar, button=2) self.assertThat(self.unity.dash.search_string, Eventually(Equals('ThirdButtonPaste'))) @@ -691,7 +689,7 @@ class DashVisualTests(DashTestCase): self.unity.dash.ensure_visible() - self.assertThat(self.unity.dash.geometry[0], Eventually(Equals(launcher.geometry[0] + launcher.geometry[2] - 1))) + self.assertThat(self.unity.dash.view.x, Eventually(Equals(launcher.geometry.x + launcher.geometry.width - 1))) def test_see_more_result_alignment(self): @@ -701,11 +699,9 @@ class DashVisualTests(DashTestCase): self.unity.dash.reveal_application_scope() scope = self.unity.dash.get_current_scope() - self.assertThat(lambda: len(scope.get_groups()), Eventually(GreaterThan(0), timeout=20)) - - groups = scope.get_groups() + self.assertThat(lambda: len(scope.get_categories()), Eventually(GreaterThan(0), timeout=20)) - for group in groups: + for group in scope.get_categories(): if (group.is_visible and group.expand_label_is_visible): expand_label_y = group.expand_label_y + group.expand_label_baseline name_label_y = group.name_label_y + group.name_label_baseline @@ -725,10 +721,7 @@ class DashScopeBarTests(DashTestCase): the rectangle outside of the icon. """ app_icon = self.scopebar.get_icon_by_name(u'applications.scope') - - self.mouse.move(app_icon.x + (app_icon.width / 2), - app_icon.y + (app_icon.height / 2)) - self.mouse.click() + self.mouse.click_object(app_icon) self.assertThat(self.scopebar.active_scope, Eventually(Equals('applications.scope'))) @@ -1119,10 +1112,7 @@ class PreviewNavigateTests(DashTestCase): cover_art = self.get_current_preview().cover_art[0] # click the cover-art (this will set focus) - tx = cover_art.x + (cover_art.width / 2) - ty = cover_art.y + (cover_art.height / 2) - self.mouse.move(tx, ty) - self.mouse.click() + self.mouse.click_object(cover_art) self.keyboard.press_and_release("Escape") @@ -1138,13 +1128,21 @@ class PreviewNavigateTests(DashTestCase): class PreviewClickCancelTests(DashTestCase): """Tests that the preview closes when left, middle, and right clicking in the preview""" + scenarios = [('Left button', {'clicked_button': 1}), + ('Middle button', {'clicked_button': 2}), + ('Right button', {'clicked_button': 3})] + def setUp(self): super(PreviewClickCancelTests, self).setUp() gettext.install("unity-scope-applications") - scope = self.unity.dash.reveal_application_scope() + scope = self.unity.dash.reveal_application_scope(clear_search=False) self.addCleanup(self.unity.dash.ensure_hidden) # Only testing an application preview for this test. - self.keyboard.type("Software Updater") + + search_string = "Software Updater" + if self.unity.dash.search_string != search_string: + self.unity.dash.clear_search() + self.keyboard.type(search_string) # wait for "Installed" category category = self.wait_for_category(scope, _("Installed")) @@ -1159,183 +1157,43 @@ class PreviewClickCancelTests(DashTestCase): self.preview_container = self.unity.dash.view.get_preview_container() - def test_left_click_on_preview_icon_cancel_preview(self): - """Left click on preview icon must close preview.""" - icon = self.get_current_preview().icon[0] - self.assertThat(icon, NotEquals(None)) - - tx = icon.x + icon.width - ty = icon.y + (icon.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=1) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_middle_click_on_preview_icon_cancel_preview(self): - """Middle click on preview icon must close preview.""" - icon = self.get_current_preview().icon[0] - self.assertThat(icon, NotEquals(None)) - - tx = icon.x + icon.width - ty = icon.y + (icon.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=2) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_right_click_on_preview_icon_cancel_preview(self): - """Right click on preview icon must close preview.""" + def test_click_on_preview_icon_cancel_preview(self): + """Clicking with any button on preview icon must close preview.""" icon = self.get_current_preview().icon[0] self.assertThat(icon, NotEquals(None)) - tx = icon.x + icon.width - ty = icon.y + (icon.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=3) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_left_click_on_preview_image_cancel_preview(self): - """Left click on preview image must cancel the preview.""" - cover_art = self.get_current_preview().cover_art[0] - self.assertThat(cover_art, NotEquals(None)) - - tx = cover_art.x + (cover_art.width / 2) - ty = cover_art.y + (cover_art.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=1) - + self.mouse.click_object(icon, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - def test_middle_click_on_preview_image_cancel_preview(self): - """Middle click on preview image must cancel the preview.""" + def test_click_on_preview_image_cancel_preview(self): + """Clicking with any button on preview image must cancel the preview.""" cover_art = self.get_current_preview().cover_art[0] self.assertThat(cover_art, NotEquals(None)) - tx = cover_art.x + (cover_art.width / 2) - ty = cover_art.y + (cover_art.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=2) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_right_click_on_preview_image_cancel_preview(self): - """Right click on preview image must cancel the preview.""" - cover_art = self.get_current_preview().cover_art[0] - self.assertThat(cover_art, NotEquals(None)) - - tx = cover_art.x + (cover_art.width / 2) - ty = cover_art.y + (cover_art.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=3) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_left_click_on_preview_text_cancel_preview(self): - """Left click on some preview text must cancel the preview.""" - text = self.get_current_preview().text_boxes[0] - self.assertThat(text, NotEquals(None)) - - tx = text.x + (text.width / 2) - ty = text.y + (text.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=1) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_middle_click_on_preview_text_cancel_preview(self): - """Middle click on some preview text must cancel the preview.""" - text = self.get_current_preview().text_boxes[0] - self.assertThat(text, NotEquals(None)) - - tx = text.x + (text.width / 2) - ty = text.y + (text.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=2) - + self.mouse.click_object(cover_art, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - def test_right_click_on_preview_text_cancel_preview(self): - """Right click on some preview text must cancel the preview.""" + def test_click_on_preview_text_cancel_preview(self): + """Clicking with any button on some preview text must cancel the preview.""" text = self.get_current_preview().text_boxes[0] self.assertThat(text, NotEquals(None)) - - tx = text.x + (text.width / 2) - ty = text.y + (text.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=3) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_left_click_on_preview_ratings_widget_cancel_preview(self): - """Left click on the ratings widget must cancel the preview.""" - ratings_widget = self.get_current_preview().ratings_widget[0] - self.assertThat(ratings_widget, NotEquals(None)) - - tx = ratings_widget.x + (ratings_widget.width / 2) - ty = ratings_widget.y + (ratings_widget.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=1) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_middle_click_on_preview_ratings_widget_cancel_preview(self): - """Middle click on the ratings widget must cancel the preview.""" - ratings_widget = self.get_current_preview().ratings_widget[0] - self.assertThat(ratings_widget, NotEquals(None)) - - tx = ratings_widget.x + (ratings_widget.width / 2) - ty = ratings_widget.y + (ratings_widget.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=2) + self.mouse.click_object(text, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - def test_right_click_on_preview_ratings_widget_cancel_preview(self): - """Right click on the ratings widget must cancel the preview.""" + def test_click_on_preview_ratings_widget_cancel_preview(self): + """Clicking with any button on the ratings widget must cancel the preview.""" ratings_widget = self.get_current_preview().ratings_widget[0] self.assertThat(ratings_widget, NotEquals(None)) - - tx = ratings_widget.x + (ratings_widget.width / 2) - ty = ratings_widget.y + (ratings_widget.height / 2) - self.mouse.move(tx, ty) - self.mouse.click(button=3) + self.mouse.click_object(ratings_widget, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - def test_left_click_on_preview_info_hint_cancel_preview(self): - """Left click on the info hint must cancel the preview.""" + def test_click_on_preview_info_hint_cancel_preview(self): + """Clicking with any button on the info hint must cancel the preview.""" info_hint = self.get_current_preview().info_hint_widget[0] self.assertThat(info_hint, NotEquals(None)) - - tx = info_hint.x + (info_hint.width / 2) - ty = info_hint.y + (info_hint.height / 8) - self.mouse.move(tx, ty) - self.mouse.click(button=1) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_middle_click_on_preview_info_hint_cancel_preview(self): - """Middle click on the info hint must cancel the preview.""" - info_hint = self.get_current_preview().info_hint_widget[0] - self.assertThat(info_hint, NotEquals(None)) - - tx = info_hint.x + (info_hint.width / 2) - ty = info_hint.y + (info_hint.height / 8) - self.mouse.move(tx, ty) - self.mouse.click(button=2) - - self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) - - def test_right_click_on_preview_info_hint_cancel_preview(self): - """Right click on the info hint must cancel the preview.""" - info_hint = self.get_current_preview().info_hint_widget[0] - self.assertThat(info_hint, NotEquals(None)) - - tx = info_hint.x + (info_hint.width / 2) - ty = info_hint.y + (info_hint.height / 8) - self.mouse.move(tx, ty) - self.mouse.click(button=3) + self.mouse.click_object(info_hint, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) @@ -1401,3 +1259,4 @@ class DashCrossMonitorsTests(DashTestCase): self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) + diff --git a/tests/autopilot/unity/tests/test_panel.py b/tests/autopilot/unity/tests/test_panel.py index 77da6a5b3..e5359a87a 100644 --- a/tests/autopilot/unity/tests/test_panel.py +++ b/tests/autopilot/unity/tests/test_panel.py @@ -78,8 +78,7 @@ class PanelTestsBase(UnityTestCase): self.keybinding("window/restore") self.addCleanup(self.keybinding, "window/maximize") - sleep(.25) - self.assertProperty(win, is_maximized=maximized) + self.assertThat(lambda: win.is_maximized, Eventually(Equals(maximized))) def open_new_application_window(self, app_name, maximized=False, move_to_monitor=True): """Opens a new instance of the requested application, ensuring that only @@ -93,8 +92,8 @@ class PanelTestsBase(UnityTestCase): app = app_win.application app_win.set_focus() - self.assertTrue(app.is_active) - self.assertTrue(app_win.is_focused) + self.assertThat(lambda: app.is_active, Eventually(Equals(True))) + self.assertThat(lambda: app_win.is_focused, Eventually(Equals(True))) self.assertThat(app.desktop_file, Equals(app_win.application.desktop_file)) if move_to_monitor: @@ -431,7 +430,6 @@ class PanelWindowButtonsTests(PanelTestsBase): self.addCleanup(self.unity.hud.ensure_hidden) self.panel.window_buttons.maximize.mouse_click() - self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) def test_hud_maximize_button_does_not_change_dash_form_factor(self): @@ -986,7 +984,7 @@ class PanelIndicatorEntryTests(PanelTestsBase): def open_app_and_get_menu_entry(self): """Open the test app and wait for the menu entry to appear.""" - self.open_new_application_window("Remmina" if self.lim else "Calculator", + self.open_new_application_window("Text Editor" if self.lim else "Calculator", maximized=self.lim) refresh_fn = lambda: len(self.panel.menus.get_entries()) @@ -1043,7 +1041,8 @@ class PanelIndicatorEntryTests(PanelTestsBase): self.assertThat(menu_entry.active, Eventually(Equals(True))) self.open_new_application_window("Text Editor") - self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) + get_active_indicator_fn = lambda: self.unity.panels.get_active_indicator() + self.assertThat(get_active_indicator_fn, Eventually(Equals(None))) def test_indicator_opens_when_dash_is_open(self): """When the dash is open and a click is on an indicator the dash @@ -1068,8 +1067,9 @@ class PanelKeyNavigationTests(PanelTestsBase): This method will wait until the active indicator has been set. """ - self.assertThat(self.panel.get_active_indicator, Eventually(NotEquals(None))) - return self.panel.get_active_indicator() + get_active_indicator_fn = lambda: self.panel.get_active_indicator() + self.assertThat(get_active_indicator_fn, Eventually(NotEquals(None))) + return get_active_indicator_fn() def test_panel_first_menu_show_works(self): """Pressing the open-menus keybinding must open the first indicator.""" @@ -1185,9 +1185,7 @@ class PanelGrabAreaTests(PanelTestsBase): self.assertProperty(text_win, is_focused=False) self.assertProperty(calc_win, is_focused=True) - self.move_mouse_over_grab_area() - self.mouse.click() - + self.mouse.click_object(self.panel.grab_area, button=1) self.assertProperty(text_win, is_focused=True) def test_lower_the_maximized_window_works(self): @@ -1198,8 +1196,7 @@ class PanelGrabAreaTests(PanelTestsBase): self.assertProperty(text_win, is_focused=True) self.assertProperty(calc_win, is_focused=False) - self.move_mouse_over_grab_area() - self.mouse.click(2) + self.mouse.click_object(self.panel.grab_area, button=2) self.assertProperty(calc_win, is_focused=True) @@ -1209,8 +1206,7 @@ class PanelGrabAreaTests(PanelTestsBase): self.addCleanup(self.unity.hud.ensure_hidden) self.keyboard.type("Hello") - self.move_mouse_over_grab_area() - self.mouse.click() + self.mouse.click_object(self.panel.grab_area) self.keyboard.type("World") self.assertThat(self.unity.hud.search_string, Eventually(Equals("HelloWorld"))) diff --git a/tests/autopilot/unity/tests/test_quicklist.py b/tests/autopilot/unity/tests/test_quicklist.py index 130b1636f..c54ccc3b8 100644 --- a/tests/autopilot/unity/tests/test_quicklist.py +++ b/tests/autopilot/unity/tests/test_quicklist.py @@ -269,7 +269,7 @@ class QuicklistKeyNavigationTests(UnityTestCase): def assertCorrectItemSelected(self, item): """Ensure the item considers itself selected and that quicklist agrees.""" self.assertThat(item.selected, Eventually(Equals(True))) - self.assertThat(self.quicklist.selected_item.id, Equals(item.id)) + self.assertThat(self.quicklist.selected_item, Equals(item)) def test_keynav_selects_first_item_when_unselected(self): """Home key MUST select the first selectable item in a quicklist.""" @@ -402,11 +402,11 @@ class QuicklistKeyNavigationTests(UnityTestCase): # Moving the mouse horizontally doesn't change the selection self.mouse.move(mouse_item.x + mouse_item.width - 10, mouse_item.y + mouse_item.height / 2) - self.assertThat(self.quicklist.selected_item.id, Equals(key_item.id)) + self.assertThat(self.quicklist.selected_item, Equals(key_item)) # Moving the mouse outside doesn't change the selection self.mouse.move(mouse_item.x + mouse_item.width + 50, mouse_item.y + mouse_item.height / 2) - self.assertThat(self.quicklist.selected_item.id, Equals(key_item.id)) + self.assertThat(self.quicklist.selected_item, Equals(key_item)) # Moving the mouse to another entry, changes the selection mouse_item = self.quicklist.selectable_items[-2] diff --git a/tests/autopilot/unity/tests/test_spread.py b/tests/autopilot/unity/tests/test_spread.py index 2ced00ddd..c3c5b769d 100644 --- a/tests/autopilot/unity/tests/test_spread.py +++ b/tests/autopilot/unity/tests/test_spread.py @@ -114,11 +114,7 @@ class SpreadTests(UnityTestCase): target_xid = not_focused.x_id [target_win] = [w for w in self.unity.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.mouse.click_object(target_win, button=1) self.assertThat(lambda: not_focused.is_focused, Eventually(Equals(True))) def test_scaled_window_closes_on_middle_click(self): @@ -129,10 +125,8 @@ class SpreadTests(UnityTestCase): target_xid = win.x_id [target_win] = [w for w in self.unity.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) + sleep(1) + self.mouse.click_object(target_win, button=2) self.assertWindowIsScaledEquals(target_xid, False) self.assertWindowIsClosed(target_xid) @@ -146,13 +140,8 @@ class SpreadTests(UnityTestCase): [target_win] = [w for w in self.unity.screen.scaled_windows if w.xid == target_xid] # Make sure mouse is over the test window - (x1, y1, w1, h1) = target_win.geometry - self.mouse.move(x1 + w1 / 2, y1 + h1 / 2) - - (x, y, w, h) = target_win.scale_close_geometry - self.mouse.move(x + w / 2, y + h / 2) - sleep(.5) - self.mouse.click() + self.mouse.move_to_object(target_win) + self.mouse.click_object(target_win.scale_close_geometry) self.assertWindowIsScaledEquals(target_xid, False) self.assertWindowIsClosed(target_xid) diff --git a/tests/autopilot/unity/tests/test_switcher.py b/tests/autopilot/unity/tests/test_switcher.py index 4876e9eb1..d302db10f 100644 --- a/tests/autopilot/unity/tests/test_switcher.py +++ b/tests/autopilot/unity/tests/test_switcher.py @@ -629,6 +629,7 @@ class SwitcherDetailsMouseTests(SwitcherTestCase): def setUp(self): super(SwitcherDetailsMouseTests, self).setUp() self.set_timeout_setting(False) + self.mouse.move(0, 0, animate=False) def test_mouse_highlights_switcher_icons(self): """ Tests that the mouse can hightlight all the switcher icons. """ @@ -653,7 +654,7 @@ class SwitcherDetailsMouseTests(SwitcherTestCase): index = 0; for cords in icon_cords: self.mouse.move(cords[0], cords[1]) - self.assertThat(index, Equals(self.unity.switcher.selection_index)) + self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(index))) index += 1 def test_mouse_clicks_activate_icon(self): @@ -691,7 +692,7 @@ class SwitcherDetailsMouseTests(SwitcherTestCase): mouse_index = self.unity.switcher.selection_index - 1 - self.unity.switcher.view.move_over_icon(mouse_index); + self.unity.switcher.view.move_over_icon(mouse_index) # Assert we are over the icon we want to hover over. self.assertThat(self.unity.switcher.view.last_icon_selected, Eventually(Equals(mouse_index))) @@ -724,11 +725,9 @@ class SwitcherDetailsMouseTests(SwitcherTestCase): self.unity.switcher.initiate(SwitcherMode.DETAIL) self.addCleanup(self.unity.switcher.terminate) - index = 0; - for icon in self.unity.switcher.view.detail_icons: + for index in range(len(self.unity.switcher.view.detail_icons)): self.unity.switcher.view.move_over_detail_icon(index) - self.assertThat(index, Equals(self.unity.switcher.detail_selection_index)) - index += 1 + self.assertThat(self.unity.switcher.detail_selection_index, Eventually(Equals(index))) def test_mouse_click_will_activate_detail_icon(self): """ diff --git a/tests/bamf-mock-application.c b/tests/bamf-mock-application.c index fd803324e..5d724716d 100644 --- a/tests/bamf-mock-application.c +++ b/tests/bamf-mock-application.c @@ -99,10 +99,24 @@ bamf_mock_application_set_icon (BamfMockApplication * self, const gchar * icon) void bamf_mock_application_set_children (BamfMockApplication * self, GList * children) { + GList *l; + g_return_if_fail (BAMF_IS_MOCK_APPLICATION (self)); - g_list_free (self->priv->children); - self->priv->children = g_list_copy (children); + for (l = self->priv->children; l;) + { + GList *next = l->next; + BamfView *view = l->data; + self->priv->children = g_list_delete_link (self->priv->children, l); + g_signal_emit_by_name (G_OBJECT (self), "child-removed", view); + l = next; + } + + for (l = g_list_last (children); l; l = l->prev) + { + self->priv->children = g_list_prepend (self->priv->children, l->data); + g_signal_emit_by_name (G_OBJECT (self), "child-added", l->data); + } } static void diff --git a/tests/mock-application.h b/tests/mock-application.h index 5b53f5b0f..312168854 100644 --- a/tests/mock-application.h +++ b/tests/mock-application.h @@ -46,6 +46,7 @@ struct MockApplicationWindow : unity::ApplicationWindow , active_(false) , urgent_(false) { + monitor.SetGetterFunction([this] { return monitor_; }); visible.SetGetterFunction([this] { return visible_; }); active.SetGetterFunction([this] { return active_; }); urgent.SetGetterFunction([this] { return urgent_; }); @@ -54,7 +55,6 @@ struct MockApplicationWindow : unity::ApplicationWindow ON_CALL(*this, type()).WillByDefault(Invoke([this] { return type_; })); ON_CALL(*this, window_id()).WillByDefault(Invoke([this] { return xid_; })); - ON_CALL(*this, monitor()).WillByDefault(Invoke([this] { return monitor_; })); ON_CALL(*this, Focus()).WillByDefault(Invoke([this] { return LocalFocus(); })); ON_CALL(*this, application()).WillByDefault(Return(unity::ApplicationPtr())); } @@ -71,7 +71,6 @@ struct MockApplicationWindow : unity::ApplicationWindow MOCK_CONST_METHOD0(type, unity::WindowType()); MOCK_CONST_METHOD0(window_id, Window()); - MOCK_CONST_METHOD0(monitor, int()); MOCK_CONST_METHOD0(application, unity::ApplicationPtr()); MOCK_CONST_METHOD0(Focus, bool()); MOCK_CONST_METHOD0(Quit, void()); @@ -144,7 +143,7 @@ struct MockApplication : unity::Application ON_CALL(*this, type()).WillByDefault(Invoke([this] { return type_; })); ON_CALL(*this, desktop_id()).WillByDefault(Invoke([this] { return desktop_file_; })); ON_CALL(*this, repr()).WillByDefault(Return("MockApplication")); - ON_CALL(*this, GetWindows()).WillByDefault(Invoke([this] { return windows_; })); + ON_CALL(*this, GetWindows()).WillByDefault(Invoke([this] () -> unity::WindowList const& { return windows_; })); ON_CALL(*this, GetSupportedMimeTypes()).WillByDefault(Return(std::vector<std::string>())); ON_CALL(*this, GetFocusableWindow()).WillByDefault(Return(unity::ApplicationWindowPtr())); ON_CALL(*this, OwnsWindow(_)).WillByDefault(Invoke(this, &MockApplication::LocalOwnsWindow)); @@ -167,7 +166,7 @@ struct MockApplication : unity::Application MOCK_CONST_METHOD0(type, unity::AppType()); MOCK_CONST_METHOD0(repr, std::string()); MOCK_CONST_METHOD0(desktop_id, std::string()); - MOCK_CONST_METHOD0(GetWindows, unity::WindowList()); + MOCK_CONST_METHOD0(GetWindows, unity::WindowList const&()); MOCK_CONST_METHOD1(OwnsWindow, bool(Window)); MOCK_CONST_METHOD0(GetSupportedMimeTypes, std::vector<std::string>()); MOCK_CONST_METHOD0(GetFocusableWindow, unity::ApplicationWindowPtr()); diff --git a/tests/test_bamf_application.cpp b/tests/test_bamf_application.cpp index fa0b72a0d..56b2052a6 100644 --- a/tests/test_bamf_application.cpp +++ b/tests/test_bamf_application.cpp @@ -56,6 +56,8 @@ struct TestBamfApplication : public testing::Test TEST_F(TestBamfApplication, GetWindows) { + ASSERT_EQ(application_.GetWindows().size(), 0); + GList* children = nullptr; for (int i = 0; i<5; ++i) { @@ -71,8 +73,7 @@ TEST_F(TestBamfApplication, GetWindows) AddFakeWindowToWM(3, true); AddFakeWindowToWM(4, false); - auto windows = application_.GetWindows(); - ASSERT_EQ(windows.size(), 5); + EXPECT_EQ(application_.GetWindows().size(), 5); g_list_free_full(children, g_object_unref); } diff --git a/unity-shared/ApplicationManager.h b/unity-shared/ApplicationManager.h index 24b97e5e9..c0f681a5e 100644 --- a/unity-shared/ApplicationManager.h +++ b/unity-shared/ApplicationManager.h @@ -80,7 +80,6 @@ public: virtual WindowType type() const = 0; virtual Window window_id() const = 0; - virtual int monitor() const = 0; // It is possible for this to be null, especially in situations where // the application is starting up or shutting down. @@ -101,6 +100,8 @@ public: return !(operator==(other)); } + nux::ROProperty<int> monitor; + nux::ROProperty<std::string> title; nux::ROProperty<std::string> icon; @@ -121,7 +122,7 @@ public: // A string representation of the object. virtual std::string repr() const = 0; - virtual WindowList GetWindows() const = 0; + virtual WindowList const& GetWindows() const = 0; virtual bool OwnsWindow(Window window_id) const = 0; virtual std::vector<std::string> GetSupportedMimeTypes() const = 0; diff --git a/unity-shared/BamfApplicationManager.cpp b/unity-shared/BamfApplicationManager.cpp index d96255af2..2a16228a7 100644 --- a/unity-shared/BamfApplicationManager.cpp +++ b/unity-shared/BamfApplicationManager.cpp @@ -145,6 +145,10 @@ WindowBase::WindowBase(ApplicationManager const& manager, [this] (BamfView*, gboolean urgent) { this->urgent.changed.emit(urgent); }); + signals_.Add<void, BamfView*>(bamf_view_, "closed", + [this] (BamfView* view) { + pool::wins_.erase(view); + }); } bool WindowBase::Focus() const @@ -170,7 +174,13 @@ AppWindow::AppWindow(ApplicationManager const& manager, glib::Object<BamfView> c : WindowBase(manager, window) , bamf_window_(glib::object_cast<BamfWindow>(window)) { + monitor.SetGetterFunction(std::bind(&AppWindow::GetMonitor, this)); maximized.SetGetterFunction(std::bind(&AppWindow::GetMaximized, this)); + + signals_.Add<void, BamfWindow*, gint, gint>(bamf_window_, "monitor-changed", + [this] (BamfWindow*, gint, gint monitor) { + this->monitor.changed.emit(monitor); + }); signals_.Add<void, BamfWindow*, gint, gint>(bamf_window_, "maximized-changed", [this] (BamfWindow*, gint old_state, gint state) { if ((old_state == BAMF_WINDOW_MAXIMIZED) != (state == BAMF_WINDOW_MAXIMIZED)) @@ -178,6 +188,11 @@ AppWindow::AppWindow(ApplicationManager const& manager, glib::Object<BamfView> c }); } +int AppWindow::GetMonitor() const +{ + return bamf_window_get_monitor(bamf_window_); +} + bool AppWindow::GetMaximized() const { return bamf_window_maximized(bamf_window_) == BAMF_WINDOW_MAXIMIZED; @@ -188,11 +203,6 @@ Window AppWindow::window_id() const return bamf_window_get_xid(bamf_window_); } -int AppWindow::monitor() const -{ - return bamf_window_get_monitor(bamf_window_); -} - WindowType AppWindow::type() const { switch (bamf_window_get_window_type(bamf_window_)) @@ -231,14 +241,15 @@ void AppWindow::Quit() const WindowManager::Default().Close(window_id()); } -Tab::Tab(ApplicationManager const& manager, glib::Object<BamfView> const& tab) - : WindowBase(manager, tab) - , bamf_tab_(glib::object_cast<BamfTab>(tab)) -{} - Tab::Tab(ApplicationManager const& manager, glib::Object<BamfTab> const& tab) : WindowBase(manager, glib::object_cast<BamfView>(tab)) , bamf_tab_(tab) +{ + monitor.SetGetterFunction([] { return -1; }); +} + +Tab::Tab(ApplicationManager const& manager, glib::Object<BamfView> const& tab) + : Tab(manager_, glib::object_cast<BamfTab>(tab)) {} Window Tab::window_id() const @@ -251,12 +262,6 @@ WindowType Tab::type() const return WindowType::TAB; } -int Tab::monitor() const -{ - // TODO, we could find the real window for the window_id, and get the monitor for that. - return -1; -} - ApplicationPtr Tab::application() const { // TODO, we could find the real window for the window_id, and return the application for that. @@ -323,6 +328,7 @@ Application::Application(ApplicationManager const& manager, glib::Object<BamfApp signals_.Add<void, BamfView*, gboolean>(bamf_view_, "running-changed", [this] (BamfView*, gboolean running) { LOG_TRACE(logger) << "running " << visible; + UpdateWindows(); this->running.changed.emit(running); }); signals_.Add<void, BamfView*, gboolean>(bamf_view_, "urgent-changed", @@ -330,21 +336,34 @@ Application::Application(ApplicationManager const& manager, glib::Object<BamfApp this->urgent.changed.emit(urgent); }); signals_.Add<void, BamfView*>(bamf_view_, "closed", - [this] (BamfView*) { + [this] (BamfView* view) { + UpdateWindows(); this->closed.emit(); + + if (!sticky()) + pool::apps_.erase(view); }); signals_.Add<void, BamfView*, BamfView*>(bamf_view_, "child-added", [this] (BamfView*, BamfView* child) { // Ownership is not passed on signals if (ApplicationWindowPtr const& win = pool::EnsureWindow(manager_, child)) - this->window_opened.emit(win); + { + if (std::find(windows_.begin(), windows_.end(), win) == windows_.end()) + { + windows_.push_back(win); + this->window_opened.emit(win); + } + } }); signals_.Add<void, BamfView*, BamfView*>(bamf_view_, "child-removed", [this] (BamfView*, BamfView* child) { if (ApplicationWindowPtr const& win = pool::EnsureWindow(manager_, child)) + { + windows_.erase(std::remove(windows_.begin(), windows_.end(), win), windows_.end()); this->window_closed.emit(win); + } }); signals_.Add<void, BamfView*, BamfView*>(bamf_view_, "child-moved", @@ -353,6 +372,8 @@ Application::Application(ApplicationManager const& manager, glib::Object<BamfApp if (ApplicationWindowPtr const& win = pool::EnsureWindow(manager_, child)) this->window_moved.emit(win); }); + + UpdateWindows(); } std::string Application::GetDesktopFile() const @@ -384,20 +405,38 @@ std::string Application::repr() const return sout.str(); } -WindowList Application::GetWindows() const +WindowList const& Application::GetWindows() const { - WindowList result; + return windows_; +} - if (!bamf_app_) - return result; +void Application::UpdateWindows() +{ + if (!bamf_app_ || !running() || bamf_view_is_closed(bamf_view_)) + { + for (auto it = windows_.begin(); it != windows_.end();) + { + window_closed.emit(*it); + it = windows_.erase(it); + } + + return; + } + + bool was_empty = windows_.empty(); std::shared_ptr<GList> children(bamf_view_get_children(bamf_view_), g_list_free); for (GList* l = children.get(); l; l = l->next) { if (ApplicationWindowPtr const& window = pool::EnsureWindow(manager_, static_cast<BamfView*>(l->data))) - result.push_back(window); + { + if (was_empty || std::find(windows_.begin(), windows_.end(), window) == windows_.end()) + { + windows_.push_back(window); + window_opened.emit(window); + } + } } - return result; } bool Application::OwnsWindow(Window window_id) const @@ -405,15 +444,13 @@ bool Application::OwnsWindow(Window window_id) const if (!window_id) return false; - bool owns = false; - std::shared_ptr<GList> children(bamf_view_get_children(bamf_view_), g_list_free); - for (GList* l = children.get(); l && !owns; l = l->next) + for (auto const& win : windows_) { - owns = BAMF_IS_WINDOW(l->data) && - bamf_window_get_xid(static_cast<BamfWindow*>(l->data)) == window_id; + if (win->window_id() == window_id) + return true; } - return owns; + return false; } std::vector<std::string> Application::GetSupportedMimeTypes() const @@ -515,7 +552,7 @@ bool Application::GetSeen() const g_quark_from_string(UNSEEN_QUARK)); } -bool Application::SetSeen(bool const& param) +bool Application::SetSeen(bool param) { bool is_seen = GetSeen(); if (param == is_seen) @@ -533,12 +570,15 @@ bool Application::GetSticky() const return bamf_view_is_sticky(bamf_view_); } -bool Application::SetSticky(bool const& param) +bool Application::SetSticky(bool param) { bool is_sticky = GetSticky(); if (param == is_sticky) return false; // unchanged + if (!param && bamf_view_is_closed(bamf_view_)) + pool::apps_.erase(bamf_view_); + bamf_view_set_sticky(bamf_view_, param); return true; // value updated } @@ -640,6 +680,12 @@ ApplicationWindowPtr Manager::GetWindowForId(Window xid) const if (xid == 0) return nullptr; + for (auto const& win_pair : pool::wins_) + { + if (win_pair.second->window_id() == xid) + return win_pair.second; + } + // TODO: use bamf_matcher_get_window_for_xid auto* app = bamf_matcher_get_application_for_xid(matcher_, xid); @@ -724,16 +770,14 @@ void Manager::OnViewClosed(BamfMatcher* matcher, BamfView* view) { if (ApplicationPtr const& app = pool::EnsureApplication(*this, view)) application_stopped.emit(app); - - pool::apps_.erase(view); } else if (BAMF_IS_WINDOW(view)) { if (ApplicationWindowPtr const& win = pool::EnsureWindow(*this, view)) window_closed.emit(win); - - pool::wins_.erase(view); } + + /* No removal here, it's done inside views, as 'closed' signal arrives later */ } } // namespace bamf diff --git a/unity-shared/BamfApplicationManager.h b/unity-shared/BamfApplicationManager.h index 6dacac12e..74503a9aa 100644 --- a/unity-shared/BamfApplicationManager.h +++ b/unity-shared/BamfApplicationManager.h @@ -49,6 +49,7 @@ public: protected: ApplicationManager const& manager_; glib::Object<BamfView> bamf_view_; + glib::SignalManager signals_; }; @@ -66,9 +67,6 @@ public: return static_cast<WindowBase const*>(this)->bamf_view_ == static_cast<WindowBase const&>(other).bamf_view_; } bool operator!=(unity::ApplicationWindow const& other) const override { return !(operator==(other)); } - -protected: - glib::SignalManager signals_; }; // NOTE: Can't use Window as a type as there is a #define for Window to some integer value. @@ -82,12 +80,13 @@ public: WindowType type() const override; Window window_id() const override; - int monitor() const override; ApplicationPtr application() const override; void Quit() const override; - bool GetMaximized() const; private: + int GetMonitor() const; + bool GetMaximized() const; + glib::Object<BamfWindow> bamf_window_; }; @@ -101,7 +100,6 @@ public: WindowType type() const override; Window window_id() const override; - int monitor() const override; ApplicationPtr application() const override; bool Focus() const override; void Quit() const override; @@ -121,7 +119,7 @@ public: virtual AppType type() const; - virtual WindowList GetWindows() const; + virtual WindowList const& GetWindows() const; virtual bool OwnsWindow(Window window_id) const; virtual std::vector<std::string> GetSupportedMimeTypes() const; @@ -145,13 +143,16 @@ private: // Property getters and setters std::string GetDesktopFile() const; bool GetSeen() const; - bool SetSeen(bool const& param); + bool SetSeen(bool param); bool GetSticky() const; - bool SetSticky(bool const& param); + bool SetSticky(bool param); + + void UpdateWindows(); private: glib::Object<::BamfApplication> bamf_app_; + WindowList windows_; glib::SignalManager signals_; std::string type_; }; diff --git a/unity-shared/IconTexture.cpp b/unity-shared/IconTexture.cpp index 901228ccf..4b3cfa9eb 100644 --- a/unity-shared/IconTexture.cpp +++ b/unity-shared/IconTexture.cpp @@ -281,8 +281,10 @@ void IconTexture::GetTextureSize(int* width, int* height) void IconTexture::SetOpacity(float opacity) { - _opacity = opacity; + if (_opacity == opacity) + return; + _opacity = opacity; QueueDraw(); } diff --git a/unity-shared/PluginAdapter.cpp b/unity-shared/PluginAdapter.cpp index 189f9480c..8979fc785 100644 --- a/unity-shared/PluginAdapter.cpp +++ b/unity-shared/PluginAdapter.cpp @@ -781,7 +781,10 @@ void PluginAdapter::UnMinimize(Window window_id) { CompWindow* window = m_Screen->findWindow(window_id); if (window && (window->actions() & CompWindowActionMinimizeMask)) + { window->unminimize(); + window->show(); + } } void PluginAdapter::Shade(Window window_id) @@ -962,6 +965,7 @@ void PluginAdapter::FocusWindowGroup(std::vector<Window> const& window_ids, if (forced_unminimize) { top_window->unminimize(); + top_window->show(); } top_window->raise(); diff --git a/unity-shared/StandaloneAppManager.cpp b/unity-shared/StandaloneAppManager.cpp index 388ada139..f3978e75a 100644 --- a/unity-shared/StandaloneAppManager.cpp +++ b/unity-shared/StandaloneAppManager.cpp @@ -81,6 +81,22 @@ std::ostream& operator<<(std::ostream &os, WindowType wt) return os; } +void connect_window_events(ApplicationWindowPtr const& win) +{ + win->title.changed.connect([win] (std::string const& t) { + std::cout << "Window "<< win->window_id()<< " title changed to "<< t << endl; + }); + win->maximized.changed.connect([win] (bool m) { + std::cout << "Window "<< win->window_id()<< " maximized changed to "<< m << endl; + }); + win->monitor.changed.connect([win] (int m) { + std::cout << "Window "<< win->window_id()<< " monitor changed to "<< m << endl; + }); + win->active.changed.connect([win] (bool a) { + std::cout << "Window "<< win->window_id()<< " active changed to "<< a << endl; + }); +} + void dump_app(ApplicationPtr const& app, std::string const& prefix = "") { if (app) @@ -153,6 +169,7 @@ void connect_events(ApplicationPtr const& app) }); app->window_opened.connect([idx](ApplicationWindowPtr const& window) { cout << "** " << names[idx] << " window opened: " << window->title() << endl; + connect_window_events(window); }); app->window_closed.connect([idx](ApplicationWindowPtr const& window) { cout << "** " << names[idx] << " window closed: " << window->title() << endl; @@ -163,21 +180,10 @@ void connect_events(ApplicationPtr const& app) app->seen = true; for (auto win : app->GetWindows()) - { - win->title.changed.connect([win] (std::string const& t) { - std::cout << "Window "<< win->window_id()<< " title changed to "<< t << endl; - }); - win->maximized.changed.connect([win] (bool m) { - std::cout << "Window "<< win->window_id()<< " maximized changed to "<< m << endl; - }); - win->active.changed.connect([win] (bool a) { - std::cout << "Window "<< win->window_id()<< " active changed to "<< a << endl; - }); - } + connect_window_events(win); } - nux::logging::Level glog_level_to_nux(GLogLevelFlags log_level) { // For some weird reason, ERROR is more critical than CRITICAL in gnome. diff --git a/unity-shared/WindowButtons.cpp b/unity-shared/WindowButtons.cpp index 613fdb1dc..6ddbc59ab 100644 --- a/unity-shared/WindowButtons.cpp +++ b/unity-shared/WindowButtons.cpp @@ -234,14 +234,14 @@ void WindowButton::AddProperties(debug::IntrospectionData& introspection) } introspection.add(GetAbsoluteGeometry()) - .add("type", type_name) - .add("visible", IsVisible() && Parent()->opacity() != 0.0f) - .add("sensitive", Parent()->GetInputEventSensitivity()) - .add("enabled", enabled()) - .add("visual_state", state_name) - .add("opacity", Parent()->opacity()) - .add("focused", Parent()->focused()) - .add("overlay_mode", overlay_mode()); + .add("type", type_name) + .add("visible", IsVisible() && Parent()->opacity() != 0.0f) + .add("sensitive", Parent()->GetInputEventSensitivity()) + .add("enabled", enabled()) + .add("visual_state", state_name) + .add("opacity", Parent()->opacity()) + .add("focused", Parent()->focused()) + .add("overlay_mode", overlay_mode()); } } // Internal Namespace |
