diff options
| -rw-r--r-- | dash/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | dash/DashController.cpp | 4 | ||||
| -rw-r--r-- | dash/DashView.cpp | 189 | ||||
| -rw-r--r-- | dash/DashView.h | 19 | ||||
| -rw-r--r-- | dash/LensView.cpp | 35 | ||||
| -rw-r--r-- | dash/LensView.h | 9 | ||||
| -rw-r--r-- | dash/PreviewStateMachine.cpp | 113 | ||||
| -rw-r--r-- | dash/PreviewStateMachine.h | 74 | ||||
| -rw-r--r-- | dash/ResultView.cpp | 13 | ||||
| -rw-r--r-- | dash/ResultView.h | 16 | ||||
| -rw-r--r-- | dash/ResultViewGrid.cpp | 89 | ||||
| -rw-r--r-- | dash/ResultViewGrid.h | 4 | ||||
| -rw-r--r-- | dash/StandaloneDash.cpp | 2 | ||||
| -rw-r--r-- | dash/previews/PreviewContainer.h | 4 | ||||
| -rw-r--r-- | plugins/unityshell/src/unityshell.h | 5 | ||||
| -rw-r--r-- | po/POTFILES.in | 6 | ||||
| -rw-r--r-- | tests/autopilot/unity/emulators/dash.py | 5 | ||||
| -rw-r--r-- | tests/autopilot/unity/tests/test_dash.py | 23 | ||||
| -rw-r--r-- | unity-shared/DashStyle.cpp | 7 | ||||
| -rw-r--r-- | unity-shared/UBusMessages.h | 9 |
20 files changed, 549 insertions, 78 deletions
diff --git a/dash/CMakeLists.txt b/dash/CMakeLists.txt index 7d801c4b3..d20a69808 100644 --- a/dash/CMakeLists.txt +++ b/dash/CMakeLists.txt @@ -51,6 +51,7 @@ set (DASH_SOURCES LensView.cpp LensViewPrivate.cpp PlacesGroup.cpp + PreviewStateMachine.cpp ResultRenderer.cpp ResultRendererHorizontalTile.cpp ResultRendererTile.cpp diff --git a/dash/DashController.cpp b/dash/DashController.cpp index e73bf724a..4d9085c7e 100644 --- a/dash/DashController.cpp +++ b/dash/DashController.cpp @@ -122,7 +122,8 @@ void Controller::RegisterUBusInterests() sigc::mem_fun(this, &Controller::OnActivateRequest)); ubus_manager_.RegisterInterest(UBUS_DASH_ABOUT_TO_SHOW, [&] (GVariant*) { EnsureDash(); }); - ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, [&] (GVariant *data) { + ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, [&] (GVariant *data) + { unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; @@ -134,6 +135,7 @@ void Controller::RegisterUBusInterests() HideDash(true); } }); + } void Controller::EnsureDash() diff --git a/dash/DashView.cpp b/dash/DashView.cpp index f1ba9ff67..1bce170e7 100644 --- a/dash/DashView.cpp +++ b/dash/DashView.cpp @@ -33,6 +33,8 @@ #include "unity-shared/KeyboardUtil.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/UBusMessages.h" +#include "unity-shared/PreviewStyle.h" + namespace unity { @@ -42,7 +44,7 @@ namespace { nux::logging::Logger logger("unity.dash.view"); - +previews::Style preview_style; } // This is so we can access some protected members in nux::VLayout and @@ -78,6 +80,9 @@ NUX_IMPLEMENT_OBJECT_TYPE(DashView); DashView::DashView() : nux::View(NUX_TRACKER_LOCATION) , home_lens_(new HomeLens(_("Home"), _("Home screen"), _("Search"))) + , preview_container_(nullptr) + , preview_displaying_(false) + , preview_navigation_mode_(previews::Navigation::NONE) , active_lens_view_(0) , last_activated_uri_("") , search_in_progress_(false) @@ -95,7 +100,7 @@ DashView::DashView() Settings::Instance().changed.connect(sigc::mem_fun(this, &DashView::Relayout)); lenses_.lens_added.connect(sigc::mem_fun(this, &DashView::OnLensAdded)); mouse_down.connect(sigc::mem_fun(this, &DashView::OnMouseButtonDown)); - + preview_state_machine_.PreviewActivated.connect(sigc::mem_fun(this, &DashView::BuildPreview)); Relayout(); home_lens_->AddLenses(lenses_); @@ -116,6 +121,65 @@ void DashView::SetMonitorOffset(int x, int y) renderer_.y_offset = y; } +void DashView::ClosePreview() +{ + preview_displaying_ = false; + RemoveChild(preview_container_.GetPointer()); + preview_container_ = nullptr; // free resources + preview_state_machine_.ClosePreview(); + QueueDraw(); +} + +void DashView::BuildPreview(Preview::Ptr model) +{ + if (!preview_displaying_) + { + preview_container_ = previews::PreviewContainer::Ptr(new previews::PreviewContainer()); + AddChild(preview_container_.GetPointer()); + preview_container_->Preview(model, previews::Navigation::NONE); // no swipe left or right + //nux::GetWindowCompositor().SetKeyFocusArea(preview_container_.GetPointer()); + + preview_container_->SetParentObject(this); + preview_container_->SetGeometry(layout_->GetGeometry()); + preview_displaying_ = true; + + // connect to nav left/right signals to request nav left/right movement. + preview_container_->navigate_left.connect([&] () { + preview_state_machine_.Reset(); + preview_navigation_mode_ = previews::Navigation::LEFT; + + // sends a message to all result views, sending the the uri of the current preview result + // and the unique id of the result view that should be handling the results + ubus_manager_.SendMessage(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, g_variant_new("(iss)", -1, stored_preview_uri_identifier_.c_str(), stored_preview_unique_id_.c_str())); + }); + + preview_container_->navigate_right.connect([&] () { + preview_state_machine_.Reset(); + preview_navigation_mode_ = previews::Navigation::RIGHT; + + // sends a message to all result views, sending the the uri of the current preview result + // and the unique id of the result view that should be handling the results + ubus_manager_.SendMessage(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, g_variant_new("(iss)", 1, stored_preview_uri_identifier_.c_str(), stored_preview_unique_id_.c_str())); + }); + } + else + { + // got a new preview whilst already displaying, we probably clicked a navigation button. + preview_container_->Preview(model, preview_navigation_mode_); // TODO + } + + if (G_LIKELY(preview_state_machine_.left_results() > 0 && preview_state_machine_.right_results() > 0)) + preview_container_->DisableNavButton(previews::Navigation::NONE); + else if (preview_state_machine_.left_results() > 0) + preview_container_->DisableNavButton(previews::Navigation::RIGHT); + else if (preview_state_machine_.right_results() > 0) + preview_container_->DisableNavButton(previews::Navigation::LEFT); + else + preview_container_->DisableNavButton(previews::Navigation::BOTH); + + QueueDraw(); +} + void DashView::AboutToShow() { ubus_manager_.SendMessage(UBUS_BACKGROUND_REQUEST_COLOUR_EMIT); @@ -127,14 +191,14 @@ void DashView::AboutToShow() if (active_lens_view_->lens()->id() == "home.lens") { for (auto lens : lenses_.GetLenses()) - { - lens->view_type = ViewType::HOME_VIEW; - LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HOME_VIEW - << " on '" << lens->id() << "'"; - } + { + lens->view_type = ViewType::HOME_VIEW; + LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HOME_VIEW + << " on '" << lens->id() << "'"; + } - home_lens_->view_type = ViewType::LENS_VIEW; - LOG_DEBUG(logger) << "Setting ViewType " << ViewType::LENS_VIEW + home_lens_->view_type = ViewType::LENS_VIEW; + LOG_DEBUG(logger) << "Setting ViewType " << ViewType::LENS_VIEW << " on '" << home_lens_->id() << "'"; } else if (active_lens_view_) @@ -147,6 +211,12 @@ void DashView::AboutToShow() // this will make sure the spinner animates if the search takes a while search_bar_->ForceSearchChanged(); + // if a preview is open, close it + if (preview_displaying_) + { + ClosePreview(); + } + renderer_.AboutToShow(); } @@ -199,6 +269,12 @@ void DashView::SetupViews() content_layout_->AddView(lenses_layout_, 1, nux::MINOR_POSITION_LEFT); home_view_ = new LensView(home_lens_, nullptr); + home_view_->uri_preview_activated.connect([&] (std::string const& uri, std::string const& unique_id) + { + stored_preview_unique_id_ = unique_id; + stored_preview_uri_identifier_ = uri; + }); + AddChild(home_view_); active_lens_view_ = home_view_; lens_views_[home_lens_->id] = home_view_; @@ -214,6 +290,17 @@ void DashView::SetupUBusConnections() { ubus_manager_.RegisterInterest(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, sigc::mem_fun(this, &DashView::OnActivateRequest)); + + ubus_manager_.RegisterInterest(UBUS_DASH_PREVIEW_INFO_PAYLOAD, [&] (GVariant *data) + { + int position = -1; + int results_to_the_left = 0; + int results_to_the_right = 0; + g_variant_get(data, "(iii)", &position, &results_to_the_left, &results_to_the_right); + preview_state_machine_.SetSplitPosition(SplitPosition::CONTENT_AREA, position); + preview_state_machine_.left_results = results_to_the_left; + preview_state_machine_.right_results = results_to_the_right; + }); } long DashView::PostLayoutManagement (long LayoutResult) @@ -249,6 +336,9 @@ void DashView::Relayout() ubus_manager_.SendMessage(UBUS_DASH_SIZE_CHANGED, g_variant_new("(ii)", content_geo_.width, content_geo_.height)); + if (preview_displaying_) + preview_container_->SetGeometry(layout_->GetGeometry()); + QueueDraw(); } @@ -291,6 +381,13 @@ nux::Geometry DashView::GetBestFitGeometry(nux::Geometry const& for_geo) void DashView::Draw(nux::GraphicsEngine& gfx_context, bool force_draw) { renderer_.DrawFull(gfx_context, content_geo_, GetAbsoluteGeometry(), GetGeometry()); + + // we only do this because the previews don't redraw correctly right now, so we have to force + // a full redraw every frame. performance sucks but we'll fix it post FF + if (preview_displaying_) + { + preview_container_->ProcessDraw(gfx_context, true); + } } void DashView::DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw) @@ -298,23 +395,28 @@ void DashView::DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw) renderer_.DrawInner(gfx_context, content_geo_, GetAbsoluteGeometry(), GetGeometry()); if (IsFullRedraw()) - { nux::GetPainter().PushBackgroundStack(); - layout_->ProcessDraw(gfx_context, force_draw); - nux::GetPainter().PopBackgroundStack(); + + if (preview_displaying_) + { + // disabled until the draw cycle in previews can be improved + //preview_container_->ProcessDraw(gfx_context, force_draw); } else { layout_->ProcessDraw(gfx_context, force_draw); } + + if (IsFullRedraw()) + nux::GetPainter().PopBackgroundStack(); renderer_.DrawInnerCleanup(gfx_context, content_geo_, GetAbsoluteGeometry(), GetGeometry()); } -void DashView::OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key) -{ - dash::Style& style = dash::Style::Instance(); - nux::Geometry geo(content_geo_); + void DashView::OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key) + { + dash::Style& style = dash::Style::Instance(); + nux::Geometry geo(content_geo_); if (Settings::Instance().GetFormFactor() == FormFactor::DESKTOP) { @@ -338,6 +440,12 @@ void DashView::OnActivateRequest(GVariant* args) std::string id(AnalyseLensURI(uri.Str())); + // we got an activation request, we should probably close the preview + if (preview_displaying_) + { + ClosePreview(); + } + if (!visible_) { lens_bar_->Activate(id); @@ -443,6 +551,13 @@ void DashView::OnLensAdded(Lens::Ptr& lens) AddChild(view); view->SetVisible(false); view->uri_activated.connect(sigc::mem_fun(this, &DashView::OnUriActivated)); + view->uri_preview_activated.connect([&] (std::string const& uri, std::string const& unique_id) + { + LOG_DEBUG(logger) << "got unique id from preview activation: " << unique_id; + stored_preview_unique_id_ = unique_id; + stored_preview_uri_identifier_ = uri; + }); + lenses_layout_->AddView(view, 1); lens_views_[lens->id] = view; @@ -459,6 +574,13 @@ void DashView::OnLensAdded(Lens::Ptr& lens) } }); + // Hook up to the new preview infrastructure + lens->preview_ready.connect([&] (std::string const& uri, Preview::Ptr model) + { + LOG_DEBUG(logger) << "Got preview for: " << uri; + preview_state_machine_.ActivatePreview(model); // this does not immediately display a preview - we now wait. + }); + // global search done is handled by the home lens, no need to connect to it // BUT, we will special case global search finished coming from // the applications lens, because we want to be able to launch applications @@ -492,6 +614,8 @@ void DashView::OnLensBarActivated(std::string const& id) << " on '" << it.first << "'"; } + search_bar_->SetVisible(true); + QueueRelayout(); search_bar_->search_string = view->search_string; search_bar_->search_hint = view->lens()->search_hint; // lenses typically return immediately from Search() if the search query @@ -693,11 +817,13 @@ bool DashView::InspectKeyEvent(unsigned int eventType, { if ((eventType == nux::NUX_KEYDOWN) && (key_sym == NUX_VK_ESCAPE)) { - if (search_bar_->search_string == "") - ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST); - else + if (preview_displaying_) + ClosePreview(); + else if (search_bar_->search_string != "") search_bar_->search_string = ""; - + else + ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST); + return true; } return false; @@ -737,11 +863,16 @@ void DashView::AddProperties(GVariantBuilder* builder) wrapper.add("form_factor", form_factor); wrapper.add("right-border-width", style.GetDashRightTileWidth()); wrapper.add("bottom-border-height", style.GetDashBottomTileHeight()); + wrapper.add("preview_displaying", preview_displaying_); } nux::Area* DashView::KeyNavIteration(nux::KeyNavDirection direction) { - if (direction == nux::KEY_NAV_DOWN && search_bar_ && active_lens_view_) + if (preview_displaying_) + { + preview_container_->KeyNavIteration(direction); + } + else if (direction == nux::KEY_NAV_DOWN && search_bar_ && active_lens_view_) { auto show_filters = search_bar_->show_filters(); auto fscroll_view = active_lens_view_->fscroll_view(); @@ -920,5 +1051,21 @@ nux::Area* DashView::FindKeyFocusArea(unsigned int key_symbol, return nullptr; } +nux::Area* DashView::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) +{ + nux::Area* view = nullptr; + if (preview_displaying_) + { + nux::Point newpos = mouse_position; + view = dynamic_cast<nux::Area*>(preview_container_.GetPointer())->FindAreaUnderMouse(newpos, event_type); + } + else + { + view = View::FindAreaUnderMouse(mouse_position, event_type); + } + + return (view == nullptr) ? this : view; +} + } } diff --git a/dash/DashView.h b/dash/DashView.h index 31a353c6e..e057e2f5e 100644 --- a/dash/DashView.h +++ b/dash/DashView.h @@ -34,6 +34,9 @@ #include "LensView.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/OverlayRenderer.h" +#include "UnityCore/Preview.h" +#include "previews/PreviewContainer.h" +#include "PreviewStateMachine.h" namespace unity { @@ -58,6 +61,9 @@ public: void OnActivateRequest(GVariant* args); void SetMonitorOffset(int x, int y); + void SetPreview(Preview::Ptr preview); + void ClosePreview(); + std::string const GetIdForShortcutActivation(std::string const& shortcut) const; std::vector<char> GetAllShortcuts(); @@ -79,7 +85,9 @@ private: void Draw(nux::GraphicsEngine& gfx_context, bool force_draw); void DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw); virtual long PostLayoutManagement (long LayoutResult); - + nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type); + + void BuildPreview(Preview::Ptr model); void OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key); void OnBackgroundColorChanged(GVariant* args); void OnSearchChanged(std::string const& search_string); @@ -106,14 +114,19 @@ private: nux::Area* KeyNavIteration(nux::KeyNavDirection direction); -private: UBusManager ubus_manager_; FilesystemLenses lenses_; HomeLens::Ptr home_lens_; LensViews lens_views_; - // View related + PreviewStateMachine preview_state_machine_; + previews::PreviewContainer::Ptr preview_container_; + bool preview_displaying_; + std::string stored_preview_unique_id_; + std::string stored_preview_uri_identifier_; + dash::previews::Navigation preview_navigation_mode_; + nux::VLayout* layout_; DashLayout* content_layout_; nux::HLayout* search_bar_layout_; diff --git a/dash/LensView.cpp b/dash/LensView.cpp index 123abc2e5..f64714746 100644 --- a/dash/LensView.cpp +++ b/dash/LensView.cpp @@ -145,11 +145,6 @@ LensView::LensView(Lens::Ptr lens, nux::Area* show_filters) filters_expanded.changed.connect([&](bool expanded) { fscroll_view_->SetVisible(expanded); QueueRelayout(); OnColumnsChanged(); }); view_type.changed.connect(sigc::mem_fun(this, &LensView::OnViewTypeChanged)); - lens_->preview_ready.connect([&] (std::string const& uri, Preview::Ptr model) - { - preview_ = previews::Preview::Ptr(new previews::Preview(model)); - }); - ubus_manager_.RegisterInterest(UBUS_RESULT_VIEW_KEYNAV_CHANGED, [&] (GVariant* data) { // we get this signal when a result view keynav changes, // its a bad way of doing this but nux ABI needs to be broken @@ -284,6 +279,8 @@ void LensView::OnCategoryAdded(Category const& category) counts_[group] = 0; ResultViewGrid* grid = new ResultViewGrid(NUX_TRACKER_LOCATION); + std::string unique_id = name + lens_->name(); + grid->unique_id = unique_id; grid->expanded = false; if (renderer_name == "tile-horizontal") { @@ -294,12 +291,25 @@ void LensView::OnCategoryAdded(Category const& category) else grid->SetModelRenderer(new ResultRendererTile(NUX_TRACKER_LOCATION)); - grid->UriActivated.connect([&] (std::string const& uri) - { - uri_activated.emit(uri); - lens_->Activate(uri); - last_activated_result_uri_ = uri; - }); + grid->UriActivated.connect(sigc::bind([&] (std::string const& uri, ResultView::ActivateType type, std::string const& view_id) + { + switch (type) + { + case ResultView::ActivateType::DIRECT: + { + uri_activated.emit(uri); + lens_->Activate(uri); + } break; + case ResultView::ActivateType::PREVIEW: + { + uri_preview_activated.emit(uri, view_id); + lens_->Preview(uri); + } break; + default: break; + }; + + }, unique_id)); + group->SetChildView(grid); /* We need the full range of method args so we can specify the offset @@ -313,6 +323,7 @@ void LensView::OnResultAdded(Result const& result) { try { PlacesGroup* group = categories_.at(result.category_index); + ResultViewGrid* grid = static_cast<ResultViewGrid*>(group->GetChildView()); std::string uri = result.uri; @@ -500,6 +511,7 @@ void LensView::Draw(nux::GraphicsEngine& gfx_context, bool force_draw) gfx_context.PushClippingRectangle(geo); nux::GetPainter().PaintBackground(gfx_context, geo); gfx_context.PopClippingRectangle(); + } void LensView::DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw) @@ -594,6 +606,5 @@ void LensView::AddProperties(GVariantBuilder* builder) .add("no-results-active", no_results_active_); } - } } diff --git a/dash/LensView.h b/dash/LensView.h index 79f6c57e5..120b4d1a7 100644 --- a/dash/LensView.h +++ b/dash/LensView.h @@ -36,7 +36,6 @@ #include "ResultViewGrid.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/PlacesVScrollBar.h" -#include "previews/Preview.h" namespace unity { @@ -70,6 +69,7 @@ public: nux::Property<bool> can_refine_search; sigc::signal<void, std::string const&> uri_activated; + sigc::signal<void, std::string const&, std::string const&> uri_preview_activated; void PerformSearch(std::string const& search_query); void CheckNoResults(Lens::Hints const& hints); @@ -93,16 +93,17 @@ private: void QueueFixRenderering(); bool FixRenderering(); + void BuildPreview(std::string const& uri, Preview::Ptr model); + virtual void Draw(nux::GraphicsEngine& gfx_context, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw); - + virtual bool AcceptKeyNavFocus(); virtual std::string GetName() const; virtual void AddProperties(GVariantBuilder* builder); std::string get_search_string() const; -private: Lens::Ptr lens_; CategoryGroups categories_; ResultCounts counts_; @@ -118,8 +119,6 @@ private: FilterBar* filter_bar_; nux::StaticCairoText* no_results_; - previews::Preview::Ptr preview_; - std::string last_activated_result_uri_; UBusManager ubus_manager_; glib::Source::UniquePtr fix_rendering_idle_; }; diff --git a/dash/PreviewStateMachine.cpp b/dash/PreviewStateMachine.cpp new file mode 100644 index 000000000..c2fe3e42c --- /dev/null +++ b/dash/PreviewStateMachine.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Gord Allott <gord.allott@canonical.com>> + */ + +#include "PreviewStateMachine.h" +#include <NuxCore/Logger.h> +namespace unity +{ +namespace dash +{ + +namespace +{ +nux::logging::Logger logger("unity.dash.PreviewStateMachine"); +} +PreviewStateMachine::PreviewStateMachine() + : preview_active(false) + , left_results(-1) + , right_results(-1) + , stored_preview_(nullptr) + , requires_activation_(true) +{ + for (int pos = SplitPosition::START; pos != SplitPosition::END; pos++) + { + split_positions_[pos] = -1; + } + + left_results.changed.connect([&] (int value) { CheckPreviewRequirementsFulfilled();}); + right_results.changed.connect([&] (int value) { CheckPreviewRequirementsFulfilled();}); +} + +PreviewStateMachine::~PreviewStateMachine() +{ +} + +void PreviewStateMachine::ActivatePreview(Preview::Ptr preview) +{ + stored_preview_ = preview; + CheckPreviewRequirementsFulfilled(); + left_results = -1; + right_results = -1; + requires_activation_ = true; +} + +void PreviewStateMachine::Reset() +{ + left_results = -1; + right_results = -1; + stored_preview_ = nullptr; + requires_activation_ = true; +} + +void PreviewStateMachine::ClosePreview() +{ + stored_preview_ = nullptr; + preview_active = true; + SetSplitPosition(SplitPosition::CONTENT_AREA, -1); +} + +void PreviewStateMachine::SetSplitPosition(SplitPosition position, int coord) +{ + split_positions_[static_cast<int>(position)] = coord; + CheckPreviewRequirementsFulfilled(); +} + +int PreviewStateMachine::GetSplitPosition(SplitPosition position) +{ + return split_positions_[static_cast<int>(position)]; +} + +void PreviewStateMachine::CheckPreviewRequirementsFulfilled() +{ + if (!requires_activation_) + return; + + if (stored_preview_ == nullptr) + return; + + /* right now this is disabled as long as we aren't doing the fancy splitting animation + * as we don't care about positions + * + if (GetSplitPosition(CONTENT_AREA) < 0) return; + if (GetSplitPosition(FILTER_BAR) < 0) return; + if (GetSplitPosition(LENS_BAR) < 0) return; + if (GetSplitPosition(SEARCH_BAR) < 0) return; + */ + + if (left_results < 0 || + right_results < 0) + return; + + LOG_DEBUG(logger) << "activating preview: " << left_results << " - " << right_results; + preview_active = true; + PreviewActivated(stored_preview_); + requires_activation_ = false; +} + +} +} diff --git a/dash/PreviewStateMachine.h b/dash/PreviewStateMachine.h new file mode 100644 index 000000000..1652c1583 --- /dev/null +++ b/dash/PreviewStateMachine.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Gord Allott <gord.allott@canonical.com>> + */ + +#ifndef UNITY_PREVIEW_STATE_MACHINE_H_ +#define UNITY_PREVIEW_STATE_MACHINE_H_ +#include "previews/Preview.h" +#include "previews/PreviewContainer.h" +#include <unordered_map> + +namespace unity +{ +namespace dash +{ + +typedef enum +{ + START, + CONTENT_AREA, + FILTER_BAR, + LENS_BAR, + SEARCH_BAR, + END +} SplitPosition; + +class PreviewStateMachine +{ +public: + PreviewStateMachine(); + ~PreviewStateMachine(); + + void ActivatePreview(Preview::Ptr preview); // potentially async call. + void Reset(); // resets the state but does not close the preview + void ClosePreview(); + + void SetSplitPosition(SplitPosition position, int coord); + int GetSplitPosition(SplitPosition position); + + nux::Property<bool> preview_active; + nux::Property<int> left_results; + nux::Property<int> right_results; + + sigc::signal<void, Preview::Ptr> PreviewActivated; + +private: + void CheckPreviewRequirementsFulfilled(); + + // all stored co-ordinates are absolute geometry + // to make dealing with the views inside the scrollview easier to understand + std::unordered_map<int, int> split_positions_; + + Preview::Ptr stored_preview_; + bool requires_activation_; + bool requires_new_position_; +}; + +} +} + +#endif diff --git a/dash/ResultView.cpp b/dash/ResultView.cpp index 1554e36ee..463fbb3c5 100644 --- a/dash/ResultView.cpp +++ b/dash/ResultView.cpp @@ -129,6 +129,19 @@ unsigned int ResultView::GetIndexForUri(const std::string& uri) return index; } +std::string ResultView::GetUriForIndex(unsigned int index) +{ + if (index >= results_.size()) + return ""; + + return results_[index].uri(); +} + +unsigned int ResultView::GetModelSize() +{ + return results_.size(); +} + long ResultView::ComputeContentSize() { return View::ComputeContentSize(); diff --git a/dash/ResultView.h b/dash/ResultView.h index 9ee6bfd74..b035e54ee 100644 --- a/dash/ResultView.h +++ b/dash/ResultView.h @@ -40,6 +40,12 @@ namespace dash class ResultView : public nux::View, public debug::Introspectable { public: + typedef enum ActivateType_ + { + DIRECT, + PREVIEW + } ActivateType; + NUX_DECLARE_OBJECT_TYPE(ResultView, nux::View); typedef std::vector<Result> ResultList; @@ -52,15 +58,15 @@ public: void AddResult(Result& result); void RemoveResult(Result& result); unsigned int GetIndexForUri(const std::string& uri); - + std::string GetUriForIndex(unsigned int); + unsigned int GetModelSize(); + ResultList GetResultList (); nux::Property<bool> expanded; nux::Property<int> results_per_row; - nux::Property<int> preview_spacer; // makes a vertical space for the preview with the value as the height - nux::Property<std::string> preview_result_uri; //for highlighting a preview - - sigc::signal<void, std::string const&> UriActivated; + nux::Property<std::string> unique_id; + sigc::signal<void, std::string const&, ActivateType> UriActivated; std::string GetName() const; void AddProperties(GVariantBuilder* builder); diff --git a/dash/ResultViewGrid.cpp b/dash/ResultViewGrid.cpp index 295bfd36c..728177520 100644 --- a/dash/ResultViewGrid.cpp +++ b/dash/ResultViewGrid.cpp @@ -52,6 +52,7 @@ ResultViewGrid::ResultViewGrid(NUX_FILE_LINE_DECL) , mouse_over_index_(-1) , active_index_(-1) , selected_index_(-1) + , activated_uri_("NULL") , last_lazy_loaded_result_(0) , last_mouse_down_x_(-1) , last_mouse_down_y_(-1) @@ -60,7 +61,6 @@ ResultViewGrid::ResultViewGrid(NUX_FILE_LINE_DECL) , mouse_last_x_(-1) , mouse_last_y_(-1) , extra_horizontal_spacing_(0) - , cached_preview_index_(-1) { SetAcceptKeyNavFocusOnMouseDown(false); @@ -71,7 +71,10 @@ ResultViewGrid::ResultViewGrid(NUX_FILE_LINE_DECL) selected_index_.changed.connect(needredraw_lambda); key_nav_focus_change.connect(sigc::mem_fun(this, &ResultViewGrid::OnKeyNavFocusChange)); - key_nav_focus_activate.connect([&] (nux::Area *area) { UriActivated.emit (focused_uri_); }); + key_nav_focus_activate.connect([&] (nux::Area *area) + { + UriActivated.emit (focused_uri_, ResultView::ActivateType::DIRECT); + }); key_down.connect(sigc::mem_fun(this, &ResultViewGrid::OnKeyDown)); mouse_move.connect(sigc::mem_fun(this, &ResultViewGrid::MouseMove)); mouse_click.connect(sigc::mem_fun(this, &ResultViewGrid::MouseClick)); @@ -98,8 +101,56 @@ ResultViewGrid::ResultViewGrid(NUX_FILE_LINE_DECL) g_variant_get (data, "(ii)", &recorded_dash_width_, &recorded_dash_height_); }); - preview_spacer.changed.connect([this] (int space) { SizeReallocate(); }); - preview_result_uri.changed.connect([this] (std::string uri) { QueueDraw(); }); + ubus_.RegisterInterest(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, [&] (GVariant* data) { + int nav_mode = 0; + gchar* uri = NULL; + gchar* proposed_unique_id = NULL; + g_variant_get(data, "(iss)", &nav_mode, &uri, &proposed_unique_id); + + if (std::string(proposed_unique_id) != unique_id()) + return; + + if (std::string(uri) == activated_uri_) + { + int current_index = GetIndexForUri(activated_uri_); + if (nav_mode == -1) // left + { + current_index--; + } + else if (nav_mode == 1) // right + { + current_index++; + } + + if (current_index < 0 || static_cast<unsigned int>(current_index) >= results_.size()) + { + LOG_ERROR(logger) << "requested to activated a result that does not exist: " << current_index; + return; + } + + // closed + if (nav_mode == 0) + { + activated_uri_ = ""; + } + else + { + activated_uri_ = GetUriForIndex(current_index); + LOG_DEBUG(logger) << "activating preview for index: " + << "(" << current_index << ")" + << " " << activated_uri_; + int left_results = current_index; + int right_results = (results_.size()) ? (results_.size() - current_index) - 1 : 0; + ubus_.SendMessage(UBUS_DASH_PREVIEW_INFO_PAYLOAD, + g_variant_new("(iii)", 0, left_results, right_results)); + UriActivated.emit(activated_uri_, ActivateType::PREVIEW); + } + } + + g_free(uri); + g_free(proposed_unique_id); + + }); SetDndEnabled(true, false); } @@ -239,7 +290,6 @@ void ResultViewGrid::SizeReallocate() if (extra_horizontal_spacing_ < 0) extra_horizontal_spacing_ = 0; - total_height += preview_spacer; // space needed for preview total_height += (padding * 2); // add padding SetMinimumHeight(total_height); @@ -587,8 +637,8 @@ void ResultViewGrid::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) offset_x = 0; offset_y = 0; } + nux::Geometry render_geo(x_position, y_position, renderer_->width, renderer_->height); -//nux::GetPainter().Paint2DQuadColor(GfxContext, render_geo, nux::color::Blue*0.20); renderer_->Render(GfxContext, results_[index], state, render_geo, offset_x, offset_y); x_position += renderer_->width + horizontal_spacing + extra_horizontal_spacing_; @@ -638,7 +688,20 @@ void ResultViewGrid::MouseClick(int x, int y, unsigned long button_flags, unsign Result result = results_[index]; selected_index_ = index; focused_uri_ = result.uri; - UriActivated.emit(result.uri); + if (nux::GetEventButton(button_flags) == nux::MouseButton::MOUSE_BUTTON3) + { + activated_uri_ = result.uri(); + UriActivated.emit(result.uri, ResultView::ActivateType::PREVIEW); + int left_results = index; + int right_results = (results_.size() - index) - 1; + //FIXME - just uses y right now, needs to use the absolute position of the bottom of the result + ubus_.SendMessage(UBUS_DASH_PREVIEW_INFO_PAYLOAD, + g_variant_new("(iii)", y, left_results, right_results)); + } + else + { + UriActivated.emit(result.uri, ResultView::ActivateType::DIRECT); + } } } @@ -660,17 +723,6 @@ uint ResultViewGrid::GetIndexAtPosition(int x, int y) if (y < padding) return -1; - if (!preview_result_uri().empty()) - { - // we have a preview so we have to deal with special positioning - std::tuple<int, int> preview_result_coord = GetResultPosition(cached_preview_index_); - if (static_cast<unsigned int>(y) > std::get<1>(preview_result_coord) + row_size) - { - // position is below the preview - y -= preview_spacer; - } - } - uint row_number = std::max((y - padding), 0) / row_size ; uint column_number = std::max((x - padding), 0) / column_size; @@ -680,7 +732,6 @@ uint ResultViewGrid::GetIndexAtPosition(int x, int y) std::tuple<int, int> ResultViewGrid::GetResultPosition(const std::string& uri) { unsigned int index = GetIndexForUri(uri); - cached_preview_index_ = index; return GetResultPosition(index); } diff --git a/dash/ResultViewGrid.h b/dash/ResultViewGrid.h index 4879b182f..6acb38de3 100644 --- a/dash/ResultViewGrid.h +++ b/dash/ResultViewGrid.h @@ -93,6 +93,8 @@ private: nux::Property<int> selected_index_; std::string focused_uri_; + std::string activated_uri_; + int last_lazy_loaded_result_; int last_mouse_down_x_; int last_mouse_down_y_; @@ -107,8 +109,6 @@ private: int extra_horizontal_spacing_; - unsigned int cached_preview_index_; - UBusManager ubus_; glib::Source::UniquePtr lazy_load_source_; glib::Source::UniquePtr view_changed_idle_; diff --git a/dash/StandaloneDash.cpp b/dash/StandaloneDash.cpp index 108b79293..cbc38743c 100644 --- a/dash/StandaloneDash.cpp +++ b/dash/StandaloneDash.cpp @@ -31,6 +31,7 @@ #include "DashView.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/DashStyle.h" +#include "unity-shared/ThumbnailGenerator.h" #define WIDTH 1024 #define HEIGHT 768 @@ -89,6 +90,7 @@ int main(int argc, char **argv) nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); // The instances for the pseudo-singletons. + unity::ThumbnailGenerator thumb_generator; unity::Settings settings; unity::dash::Style dash_style; diff --git a/dash/previews/PreviewContainer.h b/dash/previews/PreviewContainer.h index 4a975d158..6e790295f 100644 --- a/dash/previews/PreviewContainer.h +++ b/dash/previews/PreviewContainer.h @@ -76,13 +76,13 @@ public: bool AcceptKeyNavFocus(); + nux::Area* KeyNavIteration(nux::KeyNavDirection direction); + protected: void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw); void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw); void PreLayoutManagement(); - nux::Area* KeyNavIteration(nux::KeyNavDirection direction); - bool InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character); void OnKeyDown(unsigned long event_type, unsigned long event_keysym, unsigned long event_state, const TCHAR* character, unsigned short key_repeat_count); diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index 8bc218263..7ade045d0 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -63,7 +63,7 @@ #include <dlfcn.h> #include "HudController.h" - +#include "ThumbnailGenerator.h" namespace unity { @@ -336,7 +336,8 @@ private: UBusManager ubus_manager_; glib::SourceManager sources_; - + unity::ThumbnailGenerator thumb_generator; + friend class UnityWindow; }; diff --git a/po/POTFILES.in b/po/POTFILES.in index 33ce23678..273ff4f1c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -6,15 +6,15 @@ dash/FilterMultiRangeWidget.cpp dash/FilterRatingsWidget.cpp dash/LensView.cpp dash/PlacesGroup.cpp +dash/PreviewMusic.cpp +dash/PreviewMusicTrack.cpp hud/HudController.cpp hud/HudView.cpp -hud/StandaloneHud.cpp launcher/BFBLauncherIcon.cpp launcher/BamfLauncherIcon.cpp launcher/DesktopLauncherIcon.cpp launcher/DeviceLauncherIcon.cpp launcher/LauncherController.cpp -launcher/HudLauncherIcon.cpp launcher/SoftwareCenterLauncherIcon.cpp launcher/SpacerLauncherIcon.cpp launcher/TrashLauncherIcon.cpp @@ -31,8 +31,6 @@ plugins/unityshell/src/unityshell.cpp plugins/unityshell/unityshell.xml.in shortcuts/ShortcutHintPrivate.cpp shortcuts/ShortcutView.cpp -shortcuts/StandaloneShortcuts.cpp unity-shared/DashStyle.cpp unity-shared/SearchBar.cpp -unity-shared/UScreen.cpp diff --git a/tests/autopilot/unity/emulators/dash.py b/tests/autopilot/unity/emulators/dash.py index f7d315b65..514f4f083 100644 --- a/tests/autopilot/unity/emulators/dash.py +++ b/tests/autopilot/unity/emulators/dash.py @@ -82,6 +82,11 @@ class Dash(KeybindingsHelper): """Returns the searchbar attached to the dash.""" return self.view.get_searchbar() + @property + def preview_displaying(self): + """Returns true if the dash is currently displaying a preview""" + return self.view.preview_displaying; + def get_num_rows(self): """Returns the number of displayed rows in the dash.""" return self.view.num_rows diff --git a/tests/autopilot/unity/tests/test_dash.py b/tests/autopilot/unity/tests/test_dash.py index 7f932c102..0383ab84c 100644 --- a/tests/autopilot/unity/tests/test_dash.py +++ b/tests/autopilot/unity/tests/test_dash.py @@ -540,3 +540,26 @@ class CategoryHeaderTests(DashTestCase): self.mouse.click() self.assertThat(category.is_expanded, Eventually(Equals(is_expanded))) + +class PreviewInvocationTests(DashTestCase): + """Tests that previews can be opened and closed + """ + def test_open_preview_close_preview(self): + """Right clicking on any result shall open a preview, + escaping shall close the preview + """ + lens = self.dash.reveal_application_lens() + self.addCleanup(self.dash.ensure_hidden) + + category = lens.get_category_by_name("Installed") + + self.mouse.move(self.dash.view.x + 64, + category.header_y + category.header_height + 32) + + self.mouse.click(button=3) + #revealing a preview may be very slow, not sure if Eventually handles that nicely + self.assertThat(self.dash.preview_displaying, Eventually(Equals(True))) + + self.keyboard.press_and_release("Escape") + + self.assertThat(self.dash.preview_displaying, Eventually(Equals(False))) diff --git a/unity-shared/DashStyle.cpp b/unity-shared/DashStyle.cpp index 31d997f0f..8ac12c7d7 100644 --- a/unity-shared/DashStyle.cpp +++ b/unity-shared/DashStyle.cpp @@ -123,7 +123,7 @@ public: void Text(cairo_t* cr, nux::Color const& color, std::string const& label, - int font_size = -1, + int font_size, double horizMargin = 4.0, Alignment alignment = Alignment::CENTER); @@ -1355,6 +1355,10 @@ void Style::Impl::Text(cairo_t* cr, { pango_font_description_set_absolute_size(desc, text_size * PANGO_SCALE); } + else if (desc) + { + text_size = pango_font_description_get_size(desc) / PANGO_SCALE; + } PangoWeight weight; switch (regular_text_weight_) @@ -1627,7 +1631,6 @@ bool Style::SquareButton(cairo_t* cr, nux::ButtonVisualState state, int font_size, Alignment alignment, bool zeromargin) { - // sanity checks if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) return false; diff --git a/unity-shared/UBusMessages.h b/unity-shared/UBusMessages.h index 603baa19f..777217e0c 100644 --- a/unity-shared/UBusMessages.h +++ b/unity-shared/UBusMessages.h @@ -82,6 +82,15 @@ // FIXME - fix the nux focus api so we don't need this #define UBUS_RESULT_VIEW_KEYNAV_CHANGED "RESULT_VIEW_KEYNAV_CHANGED" +// for communicating positions to the preview state machine (iii) +// (split y coord in absolute geometry, results to the left, results to the right) +#define UBUS_DASH_PREVIEW_INFO_PAYLOAD "DASH_PREVIEW_INFO_PAYLOAD" + +// called when previews wish to navigate left/right or close (is) +// -1 = left, 0 = close, 1 = right, +// string is the uri string that last result activated was +#define UBUS_DASH_PREVIEW_NAVIGATION_REQUEST "DASH_PREVIEW_NAVIGATION_REQUEST" + // Sends a string datatype containing the new icon name #define UBUS_HUD_ICON_CHANGED "HUD_ICON_CHANGED" #define UBUS_HUD_CLOSE_REQUEST "HUD_CLOSE_REQUEST" |
